Как заставить элемент div автоматически изменять размер с сохранением aspect ratio (соотношения сторон)?
Я пытаюсь разместить div по центру экрана.
Этот div должен иметь определенную ширину и высоту, когда он помещается в доступное пространство окна, но он должен сжиматься, чтобы соответствовать, когда доступного пространства окна недостаточно, а также с сохранением исходного соотношения сторон (aspect ratio).
Я просмотрел множество примеров, которые работают с уменьшением ширины, но ни один из них не работает с изменением ширины и высоты в размере окна.
Вот мой текущий CSS:
* {
border: 0;
padding: 0;
margin: 0;
}
.stage_wrapper {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: gray;
}
.stage {
width: 960px;
height: 540px;
max-width: 90%;
max-height: 90%;
display: flex;
justify-content: center;
align-items: center;
background: chocolate;
object-fit: contain; /* I know this is for images, it's an example of what I'm looking for */
}
И мой текущий HTML:
<div class="stage_wrapper">
<div class="stage">
<p>Some text</p>
</div>
</div>
Здесь показан центрированный блок div с фиксированной шириной 960px и фиксированной высотой 540px
Это должны быть максимальные размеры.
Затем, если я изменю размер своего окна, чтобы иметь меньшую ширину или высоту, элемент div должен успешно сжиматься - за исключением того, что он не поддерживает original aspect ratio, и это то, что я ищу. Я хочу, чтобы он реагировал на изменения как ширины, так и высоты.
Это вообще возможно?
Свободный перевод вопроса How to make div element auto-resize maintaining aspect ratio? от участника @Francisco Zarabozo.
Ответы (1 шт):
Вот идея использования viewport unit и clamp(). Это своего рода if else, чтобы проверить, больше или меньше ширина экрана, чем высота (с учетом соотношения), и на основе результата мы делаем расчет.
В приведенном ниже коде есть две переменные cv и ch, и только одна из них будет равна 1
- Если это
cv, то ширина больше, поэтому мы устанавливаем высоту наcv, а ширина будет основываться на этой высоте, поэтому логически это равноcv / ratio - Если это
ch, то высота больше, поэтому мы устанавливаем ширину наcv, а высота будет основана на этой ширине, поэтому логически это равноch / ratio
В clamp() я использую 1vh / 1vw, которое я умножаю на 90, что эквивалентно вашим 90%, чтобы иметь 90vh / 90vw
body {
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
background: gray;
}
.stage {
--r: calc(960 / 540);
--cv: clamp(0px,(100vw - 100vh*var(--r))*10000,1vh);
--ch: clamp(0px,(100vh*var(--r) - 100vw)*10000,1vw);
height: calc((var(--cv) + var(--ch)/var(--r)) * 90 );
width: calc((var(--ch) + var(--cv)*var(--r)) * 90 );
max-width: 960px;
max-height: 540px; /* OR calc(960px/var(--r)) */
display: flex;
justify-content: center;
align-items: center;
background:
/* this gradient is a proof that the ratio is maintained since the angle is fixed */
linear-gradient(30deg,red 50%,transparent 50%),
chocolate;
}
<div class="stage">
<p>Some text</p>
</div>
Теоретически зажим можно упростить до:
--cv: clamp(0px,(1vw - 1vh*var(--r)),1vh);
--ch: clamp(0px,(1vh*var(--r) - 1vw),1vw);
Но чтобы избежать каких-либо проблем с округлением и не попадать в такие значения, как 0.x, я выбираю большое значение, чтобы оно всегда было ограничено до 1, если оно положительное.
UPDATE
Кажется, в Firefox есть ошибка, поэтому вот еще одна версия того же кода:
body {
height: 100vh;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
background: gray;
}
.stage {
--r: calc(960 / 540);
--cv: clamp(0px,(100vw - 100vh*var(--r))*100,90vh);
--ch: clamp(0px,(100vh*var(--r) - 100vw)*100,90vw);
height: calc((var(--cv) + var(--ch)/var(--r)) );
width: calc((var(--ch) + var(--cv)*var(--r)) );
max-width: 960px;
max-height: 540px; /* OR calc(960px/var(--r)) */
display: flex;
justify-content: center;
align-items: center;
background:
/* этот градиент является доказательством того, что соотношение сохраняется, поскольку угол фиксирован */
linear-gradient(30deg,red 50%,transparent 50%),
chocolate;
}
<div class="stage">
<p>Some text</p>
</div>
Свободный перевод ответа от участника @Temani Afif.