Как сделать автоматическое изменение размера элемента div с сохранением соотношения сторон?

#html #css

#HTML #css

Вопрос:

Я пытаюсь div центрировать изображение на экране. У этого div должно быть определенное width и height когда оно помещается в доступное пространство окна, но оно должно уменьшаться, чтобы соответствовать, когда доступного пространства окна недостаточно, но также, сохраняя исходное соотношение сторон.

Я просмотрел множество примеров, которые работают для уменьшения width , но ни один из них не работает для обоих width и height не изменяет размер окна.

Вот мой текущий 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 объект с фиксированным width значением 960 пикселей и фиксированным height значением 540 пикселей. Он никогда не должен быть больше этого.

Затем, если я изменю размер своего окна на меньший width или height меньший, div элемент успешно уменьшается — за исключением того, что он не поддерживает исходное соотношение сторон, и это то, что я ищу. Я хочу, чтобы он реагировал на изменения в обоих width и height .

Возможно ли это вообще?

Ответ №1:

Свойство aspect-ratio ref теперь имеет хорошую поддержку, поэтому вы можете использовать приведенное ниже:

 body {
  height: 100vh;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: gray;
}


.stage {
  --r: 960 / 540;

  aspect-ratio: var(--r);
  width:min(90%, min(960px, 90vh*(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> 


Старый ответ

Вот идея с использованием модуля просмотра и 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 , если положительное

Обновить

Похоже, в 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: 
    /* 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> 

Комментарии:

1. Это отлично работает, большое вам спасибо! Этот вид CSS действительно новый для меня. Знаете ли вы, какую обратную совместимость я могу ожидать от этого?

2. @FranciscoZarabozo Я думаю, что только IE будет иметь проблемы с моим кодом, что неудивительно, но у вас не должно быть проблем с другими браузерами. теперь хорошо поддерживаются переменные clamp и CSS.

3. @TemaniAfif Это действительно умно! Однако у меня возникли небольшие проблемы с выполнением кода. Не могли бы вы подробнее рассказать о том, что --cv и --ch есть? Даже определение полных имен переменных помогло бы. Спасибо!

4. @LandonSchropp Я уже подробно описываю, что представляют собой обе переменные 🙂 .. имя не имеет значения, я могу называть их a и b . Вам просто нужно понять логику, которую я описываю.

5. @TemaniAfif Вы нигде не говорите в своем сообщении, что --cv --ch представляют переменные and . Почему вы выбрали эти имена? Что они означают? Я пытаюсь понять вашу логику, но это сложно, потому что вы не объясняете, почему вы выбираете эти переменные. Было бы проще с четкими именами для ваших переменных. medium.com/coding-skills /…

Ответ №2:

Любое решение, использующее vw amp; vh предполагает, что ваш контейнер является областью просмотра. Но что, если вам нужен элемент, в котором почтовые ящики подходят для любого контейнера?

Другими словами, вам нужно поведение object-fit: contain для элемента, который не является <img> or <video> .

Вам нужен контейнер, который центрируется как по вертикали, так и по горизонтали:

 #container {
  display: flex;
  align-items: center;
  justify-content: center;
}
 

и содержащийся в нем объект с фиксированным aspect-ratio , который растягивается либо на его width , либо height на 100% :

 #object {
  aspect-ratio: 16/9;
  overflow: hidden;
  [dimension]: 100%;
}
 

где [dimension] width , если контейнер высокий, и height в противном случае.

Если вы заранее не знаете, будет ли он высоким или нет, потребуется немного javascript, чтобы проверить высоту и решить, какое измерение растянуть.

 function update() {
  const isTall = container.clientWidth / container.clientHeight < aspectRatio;
  object.style.width = isTall ? '100%' : 'auto';
  object.style.height = isTall ? 'auto' : '100%';
}

new ResizeObserver(update).observe(container);
 

Я не думаю, что это переключение размеров может быть выполнено в чистом CSS без javascript.

Вот демонстрация.

Ответ №3:

Возможно, используйте этот CSS:

 .stage_wrapper {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: gray;
 

}

 .stage {
width: 50vw;
height: 40vw;
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 */
 

}

Вы можете настроить код, если хотите, чтобы он был больше или меньше, но это позволит вашему div иметь фиксированное соотношение сторон.

Комментарии:

1. Похоже, это не работает только при уменьшении высоты окна.