Знакомство с object-fit
Перевод статьи Exploring object-fit с сайта hacks.mozilla.org (лицензия СС3.0), автор — Крис Миллс.
При работе с веб-документами мы довольно часто сталкиваемся с задачей отображения изображений (или видео) разного размера в одном и том же месте. Предположим, что вы пишете приложение с динамической галереей, которая позволяет пользователю загружать свои файлы. Вы не можете гарантировать, что все будут загружать изображения с одинаковым соотношением сторон. Что же делать?
Если разрешить соотношению сторон заполнять все пространство элемента-контейнера, то в большинстве случае это будет выглядеть ужасно. А кадрировать и менять размеры динамически «на лету» может оказаться не в ваших силах. (Допустим, если вы работаете с CMS и имеете права только для редактирования содержимого страницы.)
Модуль «Изображения и замещаемое содержимое в CSS» позволяет нам использовать свойства object-fit, предназначенное для решения подобных проблем, и object-position, устанавливающее положение контента внутри элемента по горизонтали и вертикали.
Эти элементы неплохо поддерживаются современными браузерами (за исключением IE). В этой статье мы рассмотрим несколько примеров их использования.
Примечание: object
-
fit
работает и с SVG-контентом, но того же эффекта можно достигнуть, если установить атрибут preserveAspectRatio
=""
в самом SVG.
Как работают object-fit и object-position?
Вы без проблем можете применять object
-
fit
к любому замещаемому элементу, например:
img { height: 100px; width: 100px; object-fit: contain; }
object
-
fit
может иметь одно из 5 значений:
1. contain
: Размер контента (например, изображения) будет изменен, чтобы отобразить его целиком с сохранением первоначального соотношения сторон, но при этом уместиться в размеры, заданные элементом.
2. fill
: Размер контента будет увеличен, чтобы заполнить размеры, заданные элементом, даже если при этом будет нарушено первоначальное соотношение сторон.
3. cover
: Сохранение соотношения сторон контента, но изменение его ширины и высоты. Таким образом, контент полностью покрывает элемент. Меньшая из двух сторон изменяется до размеров контейнера, а большая – выходит за рамки элемента и обрезается.
4. none
: Полностью игнорирует высоту или ширину, заданные контейнером, и просто использует оригинальный размер контента замещаемого элемента.
5. scale
-
down
: Размер контента задается либо как при указании none,
либо как при contain
,
смотря что из этого даст меньший размер замещаемого элемента в итоге.
object
-
position
действует так же, как background-position при работе с фоновыми изображениями. Например:
img { height: 100px; width: 100px; object-fit: contain; object-position: top 70px; }
Значения, выраженные в процентах, тоже можно использовать, но они рассчитываются относительно избытка доступного пространства – разницы между шириной элемента и итоговой отрисованной шириной замещаемого контента. Поэтому object
-
position
: 50% 50%
(значение, используемое по умолчанию) всегда будет помещать замещаемый элемент точно в центре. Кроме того, object
-
position
: 0% 0%
всегда будет означать выравнивание по левому верхнему углу, object
-
position
: 100% 100%
*всегда* означает выравнивание по нижнему правому углу, и т.д.
Примечание: перейдя по ссылке, вы можете увидеть несколько простых примеров позиционирования объектов.
Результат использования различных значений object-fit
Следующие примеры кода демонстрируют эффекты от использования различных значений object
-
fit
.
Использование object-fit: contain для размещения изображений с полями (леттербоксинг):
Леттербоксингом называются те случаи, когда вам нужно сохранить соотношение сторон изображений на странице, но при этом умещать их в одном и том же месте. К примеру, вы используете CMS, которая позволяет загружать продукцию в интернет-магазин или изображения в галерею, и контент могут загружать многие авторы. Они могут загружать файлы приблизительно нужного размера, но размеры не всегда будут в точности совпадать, а вам необходимо размещать каждое изображение в пространстве определенного размера.
Изображения с измененными пропорциями обычно ужасно выглядят, поэтому вместо этого вы можете добавить к изображению поля, с помощью параметра object
-
fit
: contain
(пример использования object-fit: contain):
img { width: 480px; height: 320px; background: black; } .contain { object-fit: contain; }
Кадрирование изображений с помощью object-fit:cover
Еще одним решением, позволяющим сохранить соотношение сторон, будет кадрирование каждого изображения до нужного размера, чтобы оно полностью покрывало элемент <
img
>
, при этом любые выступы будут скрыты. Для этого просто воспользуйтесь object
-
fit
:
cover
(пример использования object-fit: cover):
.cover { object-fit: cover; }
Изменение соотношения сторон видео с помощью object-fit: fill
С другой стороны, мы также можем взять видео и насильно изменить его соотношение сторон. Возможно, в некоторых видео, присланных вашим редактором контента, задано неправильное соотношение сторон, и вы хотите исправить его на лету, одним движением руки?
Возьмите этот кадр:
Если мы вставим его на страницу, используя этот код:
<video controls="controls" src="windowsill.webm" width="426" height="240"> … </video>
То выглядеть он будет ужасно: у видео появятся поля, потому что элемент <
video
>
всегда старается сохранить оригинальное соотношение сторон исходного файла. Чтобы исправить это, мы можем использовать object
-
fit
: fill
(пример использования object-fit: fill):
.fill { object-fit: fill; }
Это изменит оригинальное соотношение сторон и заставит кадр полностью заполнить элемент <
video
>
, чтобы он отображался корректно.
Интересные эффекты с использованием transition
Комбинирование object
-
fit
и object
-
position
с CSS transition может добавить галерее изображений или видео несколько довольно интересных эффектов. Например:
.none { width: 200px; height: 200px; overflow: hidden; object-fit: none; object-position: 25% 50%; transition: 1s width, 1s height; } .none:hover, .none:focus { height: 350px; width: 350px; }
Сначала отображается лишь небольшая часть изображения, а при выборе/наведении курсора на элемент он увеличивается, демонстрируя изображение целиком (пример использования object-fit: none).
Это происходит благодаря установке свойства object
-
fit
: none
у элемента <
img
>
. Мы заставляем контент полностью игнорировать заданные ранее ширину и высоту и позволяем выйти за пределы элемента. После этого мы используем overflow: hidden, чтобы обрезать всё, что не поместилось. Для плавного увеличения размера элемента <
img
>
при наведении курсора/выборе используется свойство transition
.
Пример галереи
Для демонстрации чуть более практичного использования object
-
fit
мы создали пример галереи:
16 изображений загружаются с помощью XHR
и вставляются в элементы img как ObjectURL
.
for(i = 1; i <= thumbs.length ; i++) { var requestObj = 'images/pic' + i + '.jpg'; retrieveImage(requestObj,i-1); } function retrieveImage(requestObj,imageNo) { var request = new XMLHttpRequest(); request.open('GET', requestObj, true); request.responseType = 'blob'; request.onload = function() { var objectURL = URL.createObjectURL(request.response); thumbs[imageNo].setAttribute('src',objectURL); thumbs[imageNo].onclick = function() { ... } } request.send(); }
В свою очередь, каждому изображению присваивается обработчик onclick
,
чтобы по клику они отображались в полном размере, заполняя экран (главному изображению, изначально имеющему CSS-свойство display:
none
,
присваивается класс blowup
, который заставляет его отобразиться и заполнить весь экран; после этого атрибут src
главного изображения получает URL того объекта, по превью которого кликнул пользователь).
thumbs[imageNo].onclick = function() { mainImg.setAttribute('src',objectURL); mainImg.className = 'blowup'; for(i = 0; i < thumbs.length; i++) { thumbs[i].className = 'thumb darken'; } }
Клик по полноразмерному изображению заставит картинку опять исчезнуть.
mainImg.onclick = function() { mainImg.className = 'main'; for(i = 0; i < thumbs.length; i++) { thumbs[i].className = 'thumb'; } }
Все размеры устанавливаются в процентах, поэтому сетка сохраняет свои пропорции при любом размере экрана.
body > div { height: 25%; } .thumb { float: left; width: 25%; height: 100%; object-fit: cover; }
Примечание: всем превью был присвоен атрибут tabindex
="0",
что позволяет переключаться между ними клавишей Tab (чтобы добавить элемент в список перехода по клавише Tab, необходимо присвоить ему tabindex
="0"
), а обработчик onclick
,
который позволяет отображать полноразмерное изображение, был продублирован обработчиком onfocus
,
чтобы обеспечить элементарный доступ с клавиатуры.
thumbs[imageNo].onfocus = function() { mainImg.setAttribute('src',objectURL); mainImg.className = 'blowup'; for(i = 0; i < thumbs.length; i++) { thumbs[i].className = 'thumb darken'; } }
Самое интересное же происходит благодаря object
-
fit
:
- У превью установлено свойство
object
-
fit
:cover
,
таким образом все они будут иметь одинаковый размер и правильное соотношение сторон, а лишнее будет обрезано. Выглядит это неплохо и создает интересный эффект при изменении размеров окна. - Основное изображение имеет свойства
object
-
fit
:contain
иobject
-
position
:center
, так что оно будет отображаться полностью, с правильным соотношением сторон и с максимально возможным размером.
P.S. Это тоже может быть интересно:
Mozilla не успела выпустить браузер с поддержкой, собственно, object-fit, но статью уже накатала? Прелестно :)
Если верить MDN, то object-fit будет поддерживаться с 36-й версии Firefox, а значит уже не за горами:)
А если верить календарю релизов Мозиллы, то официальная поддержка появится уже завтра:)