February 5, 2019

Изображения: размеры, пропорции, адаптация.


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

Соотношение сторон представляет собой дробь — отношение ширины к высоте. Возьмем, к примеру, изображение или видеофайл с разрешением FullHD 1920x1080 точек. 1920/1080 — если упрощать эту дробь, то получим 16/9, стандартное кинематографическое соотношение сторон. У изображения (или видео) с физическим размером 1024x768 точек эта пропорция равна 4/3.

Изменение размера

Добавляя изображение на страницу, мы имеем возможность задать размеры для отображения. Эти размеры не связаны с фактическими размерами картинки и могут повлиять на ее вид в браузере.

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

Интерполяция изображения

Суть интерполяции заключается в использовании имеющихся данных для получения ожидаемых значений в неизвестных точках. Интерполяция изображений работает в двух измерениях и пытается достичь наилучшего приближения в цвете и яркости пикселя, основываясь на значениях окружающих пикселей. Значения пикселей могут резко меняться от точки к точке. Чем больше вы знаете об окружающих пикселях, тем лучше сработает интерполяция. Вот почему результаты быстро ухудшаются по мере растягивания изображения, а кроме того, интерполяция никогда не сможет добавить изображению детальности, которой в нём нет.

Грубо говоря — попытавшись растянуть картинку размером 100*100 пикселей до размера 150*150 пикселей, мы заставим браузер “досочинить” недостающие пиксели, примерно глядя на соседние. Результатом будет потеря качества изображения.

Искажение пропорций

<body> 
    <img src="images/redcat.jpg" alt="Размеры не заданы"> 
    <img src="images/redcat.jpg" alt="Задана ширина" width="400"> 
    <img src="images/redcat.jpg" alt="Задана ширина и высота" width="400" height="400"> 
</body>

В примере использовалась одна и та же фотография, для первого <img> размеры явно не указаны, поэтому браузер добавил изображение в исходном виде. Для второй фотографии указана ширина 400 пикселей, что привело к уменьшению её размеров при сохранении пропорций. Третья фотография искажена, поскольку задана одинаковая ширина и высота, при том, что исходное изображение не квадратное.

Расчет пропорций

Как изменить размер изображения не исказив его? Нужно учитывать пропорции и изменив, к примеру, ширину — высоту ставить не произвольную, а в том же соотношении к ширине, что и в исходных данных. Чтобы не считать вручную — можно воспользоваться онлайн калькулятором, которых множество — http://alittlebit.ru/tools/proportsii-izobrazheniya/ , https://ciox.ru/aspect-ratio , http://dimavolkov.ru/resize/index.html . Гуглятся по запросу “пропорции изображения калькулятор”.

Изображение в блоке

Часто встречающаяся в верстке ситуация — изображение в блоке (контейнере). При этом именно контейнеру по факту задаются размеры и пропорции по макету, так как изображения клиент может заливать разные, а дизайн должен оставаться неизменным.

Таким образом, мы имеем два условных прямоугольника, один вписан в другой и каждый со своими пропорциями. Какие варианты возможны?

Идеальный вариант

Идеальный вариант — пропорции контейнера и изображения совпадают. К примеру — изображение 1280*800 пикселей и контейнер 640*400. Считаем — 1280/800=1.6, 640/400=1.6. 1.6=1.6. Profit! Это изображение можно вместить в этот контейнер без пустых зон, искажения и обрезки частей.

Реальный вариант

Однако, к сожалению, идеального в нашем мире не так много, как хотелось бы. Поэтому вариант следующий — неидеальный, но реальный. Контейнер квадратный, или приближенный к квадрату, например 420*400 пикселей. Посчитаем сразу пропорции — 420/400=1,05. Изображение наш уважаемый Пользователь загружает размером 640*480 пикселей. Пропорции выходят 640/480=4/3=1,333(3).

Имея исходные данные, рассмотрим способы размещения данного изображения в данный контейнер. Клиент может хотеть идеального совпадения, но давайте посчитаем — 1,05 = 1,333(3). Возможно ли такое равенство? Ответ можете дать сами. Математика в нашей вселенной строга, неподкупна и одна для всех.

Что ж, если идеально — никак, то как же можно? Есть ровно ТРИ СПОСОБА.

Способ первый, совсем плохой

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

Способ второй — пустое пространство

Размещение по большей величине. Берем бОльшую величину (в нашем случае — ширину) и делаем ее равной аналогичному параметру контейнера (ширине). А вот второй параметр расчитываем пропорционально, сами или воспользовавшись упомянутыми калькуляторами. Ширина контейнера — 420px. Подогнав изображение до той же величины, чтобы сохранить пропорции, высоту мы должны дать 315px.

Спонсор точного расчета — сайт http://alittlebit.ru/tools/proportsii-izobrazheniya/.

В результате картинка полностью разместится в контейнере, однако разница в пропорциях никуда не денется и появятся пустое пространство.

Способ третий — обрезка части изображения

Размещение по меньшей величине. В этом способе в качестве базовой величины мы берем меньшее число, в нашем случае — высоту. Высота контейнера — 400px. Уже знакомым калькулятором пропорционально рассчитываем второй параметр

Размещаем и видим, что контейнер теперь заполнен полностью, но часть изображения не поместилась и оказалась обрезана — разница пропорций, куда ж от нее деться.

Вывод

Если пропорции блока и изображения не совпадают, то, размещая изображение в блоке, мы должны сделать выбор — пустые зоны, обрезанные части, искажение.

Инструменты и приемы для верстальщика

Прием с background-image — как НЕЛЬЗЯ делать

Проблема адаптивного контейнера

В макетах адаптивного дизайна не редкость трансформация пропорций контейнера изображения на разных разрешениях. К примеру — на десктопе он квадратный, на планшете растягивается на всю ширину и становится прямоугольным, а на низком разрешении для мобильных снова теряет ширину и приближается к квадрату. Как же разместить изображение, ведь его пропорции неизменны?

Велик соблазн задать контейнеру нужные размеры, а изображение разместить задним фоном, через свойство css background-image. Это очень плохо, то, что называется bad practice, делать подобное нельзя никогда. Точнее можно, но только в одном случае — если изображение в самом деле несет функцию исключительно фоновую, декоративную. Если смысловая нагрузка размещенного изображения отличается от нуля (изображение товара, картинка в контенте, иллюстрация в редактируемом блоке) — оно не должно быть фоном.

Почему нельзя использовать background-image для изображений со смыслом?

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

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

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

Если вам досталась правка сайта с background-image в блоке

Если вам так повезло, что перед вами именно такой сайт, с разносторонней адаптацией блока и изображением в нем, подставленным через стили — можно применить лайфхак: не трогать стили и адаптацию — наверняка сайт выглядит красиво, а наш принцип “не навреди”. Вместо этого добавьте в блок тег <img>, выведите туда изображение, родителю установите overflow: hidden; (чтобы не поломалась верстка), и задайте изображению opacity: 0;

Что же получается? И семантика соблюдена (изображение в блоке есть, может быть индексировано и распарсено), и хитрую адаптацию переделывать не пришлось. НО! Это лишь для правок, при верстке настоящие индейцы так не делают!

Адаптивный блок с изображением/видео (неизменные пропорции)

Если изображение, которое нужно адаптировать, сохраняет неизменные пропорции на любом разрешении, или это видео, где пропорции по определению не должны меняться — задача проста. Все, что нам нужно — чтобы при изменении ширины блока, вложенное в него изображение (или видео) изменялось пропорционально.

Приступим.

1. Изображение (видео) обернем в отдельный блок, если это еще не сделано. Именно он и будет адаптироваться.

<div class="thumb-wrap">
   <iframe width="560" height="315" src="https://www.youtube.com/embed/Y421bWMelqE" frameborder="0" allowfullscreen></iframe>
</div>

2. Пропишем стили:

.thumb-wrap {
  position: relative;
  padding-bottom: 56.25%; /* задаёт высоту контейнера для 16:9 (если 4:3 — поставьте 75%) */
  padding-top: 30px;
  height: 0;
  overflow: hidden;
}
.thumb-wrap iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

Паддинг в процентах задан в расчете на соотношение сторон видео. Если у вас изображение — поделите большую величину (ширину или высоту) на меньшую и умножьте на 100, полученное число и есть нужный процент отступа для сохранения пропорций.

Имитация поведения background-size с применением javascript (для настоящих индейцев)

Визуально самый приятный эффект дает размещение изображения задним фоном, но так делать нельзя, мы же договорились? А нельзя ли добиться того же визуального эффекта, но с изображением, добавленным в коде HTML? Можно, если перейти на следующий уровень и задействовать манипуляции jQuery.

Использовать мы будем очень небольшой (1,9kb) плагин. Написан он был давно, когда в ходу был IE версии ниже 9, который не понимал прекрасное свойство background-size. Несмотря на почтенный возраст, и немного не те цели применения изначально, он — то что нам нужно. Приступим.

Страница проекта на GitHUB https://github.com/aramk/CSS-Background-Size-jQuery-Plugin

1. Рективацию скрипта повесим на ресайз окна. Первый вызов осуществим имитацией ресайза. Простой вызов события resize(), как показала практика, срабатывает не всегда безотказно. Чтобы он срабатывало всегда и везде — создадим пользовательское событие (его можно применять не только для описываемой ситуации, но и всегда, когда нужно ):

//Universal resize event 
function universalResize() { 
 if(typeof(Event) === 'function') {
  // modern browsers 
  window.dispatchEvent(new Event('resize')); 
 } else { 
  // for IE and other old browsers 
  // causes deprecation warning on modern browsers 
  var evt = window.document.createEvent('UIEvents'); 
  evt.initUIEvent('resize', true, false, window, 0); 
  window.dispatchEvent(evt); 
 } 
};

Теперь при вызове функции universalResize() всегда выстрелит событие изменения размера окна.

Аналогичным образом, кстати, можно создать ЛЮБОЕ кастомное событие, и вызывать его для срабатывания функций-обработчиков.

2. Подключаем плагин к селектору класса:

$window.resize(function() {
 $('.background-size-cover').bgdSize('cover');
$('.background-size-contain').bgdSize('contain');
});
// Вызываем вышеупомянутую функцию. 
universalResize();

Если блоки с изображения подтягиваются по ajax — то на срабатывание события complete добавляем такую инициализацию:

complete: function() {
 $(".background-size-cover").one("load", function() {
        $(this).removeAttr('style').bgdSize('cover');
    }).each(function() {
        if(this.complete) $(this).load();
    });
$(".background-size-contain").one("load", function() {
        $(this).removeAttr('style').bgdSize('contain');
    }).each(function() {
        if(this.complete) $(this).load();
    });
}

Такая хитрая запись позволяет вызвать функцию именно после загрузки изображения, а не после построения DOM. Берите на заметку.

Все готово! Изображениям, которым требуется поведение аналогичное background-size просто добавляем классы .background-size-cover или .background-size-contain.

Стили для контейнеров и изображений

Часто мы встречаемся с ситуацией, когда изображение обернуто в тег <a href=”…….”> для срабатывания различных галерей и тому подобного. Подобное решение требует некоторого внимания в стилях. Изображение, точнее тег <img> — элемент строчно-блочный (inline-block), в то время как <a href=”…….”> — просто инлайновый. Оборачивать inline-block в строку не семантично, а значит элементу <a> мы должны задать свойство display:inline-block;. Кроме семантики тут есть и элементарная логика — изображение имеет параметры высоты и ширины, и оборачивать его в СТРОКУ неверно. К тому же зачастую ширина и высота изображения относительны (задаются в процентах), а какие проценты ширины можно взять от строки?

Еще одна практика — задавать img стили {width: 100%; height: auto;}. И все идет хорошо, пока ширина изображения больше ширины контейнера и изображение имеет ширину бОльшую чем высоту. Однако как только в контейнер попадает изображение с исходными размерами меньше, чем сам контейнер — его растягивает и оно становится как минимум некрасивым. Если же его высота больше, чем ширина (двери, оконные рамы, одежда) то часть изображения еще и выйдет за пределы контейнера, попытавшись увеличиться пропорционально ширине.

Чтобы таких неприятных неожиданностей не случалось — не задавайте ширину жестко, пропишите {max-width: 100%; max-height: 100%}.