При работе с веб-документами мы довольно часто сталкиваемся с задачей отображения изображений (или видео) разного размера в одном и том же месте. Предположим, что вы пишете приложение с динамической галереей, которая позволяет пользователю загружать свои файлы. Вы не можете гарантировать, что все будут загружать изображения с одинаковым соотношением сторон. Что же делать?
Если разрешить соотношению сторон заполнять все пространство элемента-контейнера, то в большинстве случае это будет выглядеть ужасно. А кадрировать и менять размеры динамически «на лету» может оказаться не в ваших силах. (Допустим, если вы работаете с 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:
img {
width: 480px;
height: 320px;
background: black;
}
.contain {
object-fit: contain;
}
Кадрирование изображений с помощью object-fit:cover
Еще одним решением, позволяющим сохранить соотношение сторон, будет кадрирование каждого изображения до нужного размера, чтобы оно полностью покрывало элемент <img>, при этом любые выступы будут скрыты. Для этого просто воспользуйтесь 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:
.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 у элемента <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, так что оно будет отображаться полностью, с правильным соотношением сторон и с максимально возможным размером.
