Адаптивные изображения без шаманства

в 11:01, , рубрики: адаптивный дизайн, Блог компании «Кельник», веб-дизайн, Веб-разработка, метки:

Все, кто сталкивался с задачей сделать адаптивную графику, знает, что решений уже масса, но никакого единогласно принятого не существует. Зачастую выбор и применение решения для адаптивных изображений становятся головной болью фронтенд-разработчиков. Им приходится заменять src картинок, подгружать однопиксельные изображения и лепить прочие костыли. Нас это не устроило и мы решили сделать свой мотороллер.

На типичных сайтах изображения могут появляться тремя способами.

  1. Быть элементами дизайна сайта (бекграунды, кнопки и т.д.).
  2. Загружаться через специальные модули (например, изображения в фотоальбом).
  3. Вставляться через WYSIWYG-редактор CMS (например, в текст статьи).

Адаптивные изображения без шаманства
Мы захотели получить такое решение, которое было бы некой «надстройкой» над сайтом. Чтобы можно было не лезть в код CMS, через которую загружаются изображения на сайт, а также не готовить адаптивные картинки вручную.

Сначала на помощь приходит реализация Adaptive Images

Метод Adaptive Images

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

  1. Небольшой яваскрипт записывает в куки максимальное значение из ширины/высоты устройства. Предполагается, что картинку больше данного размера показывать нет смысла.
  2. С помощью директивы в .htaccess идет рерайт всех картинок сайта на php-скрипт adaptive-images.php.
  3. В php-скрипте есть конфиг разрешений (связанный с media queries стилей). Значение из куки подгоняется под ближайшее в большую сторону значение из этого конфига. Если изображение по запрашиваемому пути существует и его ширина больше требуемой — изображение пережимается и кладется в специальную папку кеша (если оно не было пережато заранее).
  4. Скрипт отдает картинку клиенту.

Плюсы Adaptive Images

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

Минусы Adaptive Images

А теперь немного о грустном. Данное решение подразумевает, что все (вообще все) картинки на сайте будет отдавать не nginx, не apachе, а php-скрипт. Каждая картинка — это запуск интерпретатора php (даже если картинка уже пережата). Это и медленно, и идеологически неверно.

Мы захотели сохранить плюсы данного метода и избавиться от такого ужасного недостатка.

Наш вариант

Главная идея: не запускать php-скрипт, если изображение уже существует. Для этого apache в момент редиректа должен знать название пережатой под данное разрешение картинки. Это значит, что определение разрешения должно быть переложено с php на js. Таки образом js должен не просто вычислить максимальное значение из ширины/высоты устройства, а также определить требуемое разрешение, и именно его уже записать в куку.

Также, чтобы apache мог проверить наличие картинки, он должен знать правило, по которому сохраняются пережатые изображения (в частности, название папки кеша), которое вообще говоря определено в php-скрипте.

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

  • Массив разрешений должен быть продублирован в js-скрипт.
  • Папка кеша и правило сохранения картинок должно быть продублировано в .htaccess.

Что получается

Модификация js-скрипта: adaptive.js

Здесь js, как и раньше, берет максимальное значение из высоты и ширины устройства. Определяет, есть ли модификатор плотности пикселей (ретина/не ретина), и на основе этих данных записывает в куки resolution.

Инструкции по рерайту: .htaccess

Рерайт стал чуть сложнее, но теперь проверяет наличие пережатой картинки до того, как обратиться к бекенду.

RewriteCond %{REQUEST_URI} ^/upload/iblock/.+.(?:jpe?g|gif|png)$

Правило работает только на картинки из директории /upload/iblock/.

RewriteCond %{REQUEST_FILENAME} -f

Причем, только на реально существующие картинки, в отличие от оригинала.

RewriteCond %{HTTP:Cookie} (^|; *)resolution=([1-9][0-9]+)

Правило сработает только при наличии цифровой куки resolution. Если ее нет, веб-сервер отдаст оригинал изображения.

RewriteRule .* /images_adaptive/%2%{REQUEST_URI} [L]

Осуществляем переход в папку с кешированными изображениями, полагая, что эта папка называется images_adaptive. Дальше следуют разрешение и запрашиваемый путь оригинала. То есть, если пришел запрос на /images/photo.jpg, а разрешение пользователя подсчитано как 1024, то адаптивная картинка будет расположена по пути /images_adaptive/1024/images/photo.jpg.

RewriteCond %{REQUEST_URI} ^/images_adaptive/.+.(?:jpe?g|gif|png)$

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

RewriteCond %{REQUEST_FILENAME} !-f

Если такого файла все еще нет, то есть картинка еще не была пережата в нужный размер (и тут мы убиваем ненужные запросы в php при повторных обращениях к картинке).

RewriteRule .* ai.php [L]

Направляем запрос в наш php-скрипт, который найдет, пережмет при необходимости, и отдаст нужную картинку.

Скрипт-обработчик запросов: ai.php

Убрано определение разрешения. Нужно помнить, что в этом скрипте массив разрешений должен совпадать с аналогичным в adaptive.js, а путь к папке кеша должен совпадать с используем в правиле .htaccess.

Минусы нашего форка

  • Избыточность хранимых данных. Если у нас есть одна картинка и 5 желаемых размеров, в которые ее нужно пережать, то сервер будет хранить в самом плохом случае все 6 изображений (оригинал и 5 копий). При этом, даже если картинку пережимать не нужно (скажем, она 300×100, а минимальное разрешение 480), то картинка все равно будет «пережата», то есть скопирована, 5 раз. Под каждое разрешение, чтобы избежать отдачи статики через php.
  • Обновление адаптивных изображений. Когда оригинал картинки обновится, скрипт-обработчик ничего об этом не узнает. Тут надо думать, подходит ли это для каждого конкретного случая, и как с этим бороться. Периодически очищать кеш вовсе, или что-то еще.
  • Дублирование информации в php, js и .htaccess

Решение не стало идеальным, но несмотря на минусы, мы добились желаемого результата, освободив фронтенд-разработчиков от лишней работы.

Автор: Neznaikos

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js