Сохранить соотношение сторон div, но заполнить ширину и высоту экрана в CSS?

122

У меня есть сайт, который нужно собрать, с фиксированным соотношением сторон примерно 16:9альбомного, как видео.

Я хочу, чтобы он был центрирован и расширен, чтобы заполнить доступную ширину и доступную высоту, но никогда не увеличиваться с обеих сторон.

Например:

  1. На высокой и тонкой странице содержимое будет растягиваться на всю ширину при сохранении пропорциональной высоты.
  2. На короткой широкой странице содержимое будет занимать всю высоту с пропорциональной шириной.

Я рассмотрел два метода:

  1. Используйте изображение с правильным соотношением сторон, чтобы расширить контейнер div, но я не мог заставить его вести себя одинаково в основных браузерах.
  2. Установка пропорционального нижнего отступа, но это работает только относительно ширины и игнорирует высоту. Он просто увеличивается с шириной и отображает вертикальные полосы прокрутки.

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

Любые идеи?

Генри Гибсон
источник
sitepoint.com/… Установить контейнер div наposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Уджвал Сингх

Ответы:

280

Используйте новые единицы просмотра CSS vw и vh(ширина области просмотра / высота области просмотра)

FIDDLE

Измените размер по вертикали и горизонтали, и вы увидите, что элемент всегда будет заполнять максимальный размер области просмотра без нарушения соотношения и без полос прокрутки!

(ЧИСТЫЙ) CSS

div
{
    width: 100vw; 
    height: 56.25vw; /* height:width ratio = 9/16 = .5625  */
    background: pink;
    max-height: 100vh;
    max-width: 177.78vh; /* 16/9 = 1.778 */
    margin: auto;
    position: absolute;
    top:0;bottom:0; /* vertical center */
    left:0;right:0; /* horizontal center */
}

Если вы хотите использовать максимум 90% ширины и высоты области просмотра: FIDDLE

Кроме того, поддержка браузеров тоже неплохая: IE9 +, FF, Chrome, Safaricaniuse.

Danield
источник
1
Это действительно хорошее чистое решение. Хотя в идеале я мог бы обойтись поддержкой браузера немного раньше. Я предполагаю, что в более старом браузере у вас была бы минимальная ширина / высота и установите ее на это - если бы у него были полосы прокрутки, пусть будет так,
Генри Гибсон
8
Хм .. спасибо, что сэкономили мне 2 дня работы и бесконечное количество разочарований пользователей. 1000+, если бы я мог.
Schoening
2
Ты только что сэкономил мне столько времени. Большое вам спасибо.
Дилан Гэтти
1
К сожалению, в последнее время IOS версия не поддерживает , или сломанные реализации видовых единиц ( vh, vw, vmin, vmax). Однако есть обходной путь , который использует медиа-запросы для нацеливания на устройства iOS.
Тим
1
Я тебя люблю. отличное решение!
Гютемберг
20

Просто переформулируйте Danieldответ в миксине LESS для дальнейшего использования:

// Mixin for ratio dimensions    
.viewportRatio(@x, @y) {
  width: 100vw;
  height: @y * 100vw / @x;
  max-width: @x / @y * 100vh;
  max-height: 100vh;
}

div {

  // Force a ratio of 5:1 for all <div>
  .viewportRatio(5, 1);

  background-color: blue;
  margin: 0;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
}
Кристоф Маруа
источник
11

Используйте медиа-запрос CSS @media вместе с единицами области просмотра CSS vw и vhадаптируйте к соотношению сторон области просмотра. Это дает возможность обновлять и другие свойства, например font-size.

Эта скрипка демонстрирует фиксированное соотношение сторон для div и текст, точно соответствующий его масштабу.

Исходный размер основан на полной ширине:

div {
  width: 100vw; 
  height: calc(100vw * 9 / 16);

  font-size: 10vw;

  /* align center */
  margin: auto;
  position: absolute;
  top: 0px; right: 0px; bottom: 0px; left: 0px;

  /* visual indicators */
  background:
    url(data:image/gif;base64,R0lGODlhAgAUAIABAB1ziv///yH5BAEAAAEALAAAAAACABQAAAIHhI+pa+EPCwA7) repeat-y center top,
    url(data:image/gif;base64,R0lGODlhFAACAIABAIodZP///yH5BAEAAAEALAAAAAAUAAIAAAIIhI8Zu+nIVgEAOw==) repeat-x left center,
    silver;
}

Затем, когда область просмотра шире, чем желаемое соотношение сторон, переключитесь на высоту в качестве базовой меры:

/* 100 * 16/9 = 177.778 */
@media (min-width: 177.778vh) {
  div {
    height: 100vh;
    width: calc(100vh * 16 / 9);

    font-size: calc(10vh * 16 / 9);
  }
}

Это очень похоже на вене к max-widthи max-height подход по @Danield, только более гибким.

Павел
источник
7

Мой первоначальный вопрос для этого заключался в том, как иметь элемент фиксированного аспекта, но точно уместить его в указанном контейнере, что делает его немного неудобным. Если вы просто хотите, чтобы отдельный элемент сохранял свое соотношение сторон, это намного проще.

Лучший метод, с которым я столкнулся, - это присвоить элементу нулевую высоту, а затем использовать процентное заполнение-основание, чтобы задать ему высоту. Отступ в процентах всегда пропорционален ширине элемента, а не его высоте, даже если это верхнее или нижнее отступы.

Свойства заполнения W3C

Таким образом, используя это, вы можете дать элементу процентную ширину для размещения в контейнере, а затем заполнить его, чтобы указать соотношение сторон или, другими словами, соотношение между его шириной и высотой.

.object {
    width: 80%; /* whatever width here, can be fixed no of pixels etc. */
    height: 0px;
    padding-bottom: 56.25%;
}
.object .content {
    position: absolute;
    top: 0px;
    left: 0px;
    height: 100%;
    width: 100%;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    padding: 40px;
}

Итак, в приведенном выше примере объект занимает 80% ширины контейнера, а его высота составляет 56,25% от этого значения. Если его ширина была 160 пикселей, то нижний отступ, и, таким образом, высота была бы 90 пикселей - соотношение сторон 16: 9.

Небольшая проблема, которая может не быть для вас проблемой, заключается в том, что внутри вашего нового объекта нет естественного пространства. Если вам нужно вставить какой-то текст, например, и этот текст должен принимать собственные значения заполнения, вам нужно добавить контейнер внутри и указать там свойства.

Также модули vw и vh не поддерживаются в некоторых старых браузерах, поэтому принятый ответ на мой вопрос может быть для вас невозможен, и вам, возможно, придется использовать что-то более низкое.

Генри Гибсон
источник
Чтобы избежать использования дополнительной разметки здесь, вы можете добавить элемент, используя :: before или :: after, и присвоить этому элементу высоту 0 и padding-bottom относительно ширины объекта и ширины 0. Это будет иметь тот же эффект, но с меньшим количеством разметки, и сделает всю область объекта пригодной для размещения содержимого без использования контейнера.
Генри Гибсон
6

Насколько я понимаю, вы просили CSSконкретное решение. Чтобы сохранить соотношение сторон, вам нужно разделить высоту на желаемое соотношение сторон. 16: 9 = 1,777777777778.

Чтобы получить правильную высоту для контейнера, вам нужно разделить текущую ширину на 1,777777777778. Поскольку вы не можете проверить widthконтейнер с помощью простого CSSили разделенного на процентное значение CSS, это невозможно безJavaScript (насколько мне известно).

Я написал рабочий скрипт, который сохранит желаемое соотношение сторон.

HTML

<div id="aspectRatio"></div>

CSS

body { width: 100%; height: 100%; padding: 0; margin: 0; }
#aspectRatio { background: #ff6a00; }

JavaScript

window.onload = function () {
    //Let's create a function that will scale an element with the desired ratio
    //Specify the element id, desired width, and height
    function keepAspectRatio(id, width, height) {
        var aspectRatioDiv = document.getElementById(id);
        aspectRatioDiv.style.width = window.innerWidth;
        aspectRatioDiv.style.height = (window.innerWidth / (width / height)) + "px";
    }

    //run the function when the window loads
    keepAspectRatio("aspectRatio", 16, 9);

    //run the function every time the window is resized
    window.onresize = function (event) {
        keepAspectRatio("aspectRatio", 16, 9);
    }
}

Вы можете использовать functionснова, если хотите отобразить что-то еще с другим соотношением, используя

keepAspectRatio(id, width, height);
MCSharp
источник
Спасибо за Ваш ответ. Сделать это в сценарии относительно просто, но я действительно хотел решение на основе CSS. Мне нужно действительно быстрое и надежное адаптивное решение, и иногда я нахожу, что изменение размера событий в разных браузерах немного тормозит. В конечном итоге вы выполняете преобразование немного после изменения размера браузера, поэтому это не дает наилучшего взаимодействия с пользователем. Хотите что-то, что отображает момент изменения размера браузера, следовательно, чистый CSS.
Генри Гибсон
1
@Henry Gibson Скрипт был протестирован в Chrome, FF, IE, Opera и Safari. Все работает быстро и надежно даже в старых версиях браузеров. Каждый браузер запускает свои собственные события. window.onresizeзапускается в момент изменения размера браузера.
MCSharp
помните, конечные пользователи не
меняют
3

Теперь для решения этой проблемы указано новое свойство CSS: object-fit .

http://docs.webplatform.org/wiki/css/properties/object-fit

Поддержка браузеров все еще несколько отсутствует ( http://caniuse.com/#feat=object-fit ) - в настоящее время в некоторой степени работает в большинстве браузеров, кроме Microsoft, но со временем это именно то, что требовалось для ответа на этот вопрос.

Генри Гибсон
источник
1

Ответ Даниилда хорош, но его можно использовать только тогда, когда div заполняет всю область просмотра, или, используя немного calc, можно использовать, если ширина и высота другого содержимого в области просмотра известны.

Однако, объединив margin-bottomтрюк с методом в вышеупомянутом ответе, проблема может быть сведена к простому знанию высоты другого контента. Это полезно, если у вас есть заголовок с фиксированной высотой, но, например, ширина боковой панели неизвестна.

body {
  margin: 0;
  margin-top: 100px; /* simulating a header */
}

main {
  margin: 0 auto;
  max-width: calc(200vh - 200px);
}

section {
  padding-bottom: 50%;
  position: relative;
}

div {
  position:absolute;
  background-color: red;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}
<main>
  <section>
    <div></div>
  </section>
</main>

Здесь он находится в jsfiddle с использованием scss , что делает более очевидным, откуда берутся значения.

Кэмерон Мартин
источник
0

Основываясь на ответе Дэниела , я написал этот миксин SASS с интерполяцией ( #{}) для iframe видео YouTube, который находится внутри модального диалогового окна Bootstrap:

@mixin responsive-modal-wiframe($height: 9, $width: 16.5,
                                $max-w-allowed: 90, $max-h-allowed: 60) {
  $sides-adjustment: 1.7;

  iframe {
    width: #{$width / $height * ($max-h-allowed - $sides-adjustment)}vh;
    height: #{$height / $width * $max-w-allowed}vw;
    max-width: #{$max-w-allowed - $sides-adjustment}vw;
    max-height: #{$max-h-allowed}vh;
  }

  @media only screen and (max-height: 480px) {
    margin-top: 5px;
    .modal-header {
      padding: 5px;
      .modal-title {font-size: 16px}
    }
    .modal-footer {display: none}
  }
}

Использование:

.modal-dialog {
  width: 95vw; // the modal should occupy most of the screen's available space
  text-align: center; // this will center the titles and the iframe

  @include responsive-modal-wiframe();
}

HTML:

<div class="modal">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">Video</h4>
      </div>
      <div class="modal-body">
        <iframe src="https://www.youtube-nocookie.com/embed/<?= $video_id ?>?rel=0" frameborder="0"
          allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
          allowfullscreen></iframe>
      </div>
      <div class="modal-footer">
        <button class="btn btn-danger waves-effect waves-light"
          data-dismiss="modal" type="button">Close</button>
      </div>
    </div>
  </div>
</div>

Это всегда будет отображать видео без тех черных частей, которые youtube добавляет в iframe, если ширина или высота не пропорциональны видео. Ноты:

  • $sides-adjustment это небольшая регулировка, чтобы компенсировать крошечную часть этих черных частей, которые видны по бокам;
  • на устройствах с очень низкой высотой (<480 пикселей) стандартный модальный экран занимает много места, поэтому я решил:
    1. скрыть .modal-footer;
    2. отрегулируйте расположение .modal-dialog;
    3. уменьшить размеры файла .modal-header.

После долгих испытаний теперь у меня это работает безупречно.

CPHPython
источник
-1

здесь решение, основанное на решении @Danield, которое работает даже в div.

В этом примере я использую Angular, но он может быстро перейти на стандартный JS или другой фреймворк.

Основная идея - просто переместить решение @Danield в пустой iframe и скопировать размеры после изменения размера окна iframe.

Этот iframe должен соответствовать размерам своего родительского элемента.

https://stackblitz.com/edit/angular-aspect-ratio-by-iframe?file=src%2Findex.html

Вот несколько скриншотов:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Анхель Фрага Пароди
источник