Оптимизация изображений с использованием формата WebP

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

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

Практическая реализация будет представлена на примере использования в теме оформления для Drupal 8, но принцип, лежащий в основе, будет общим для любой технологии.

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

Но, думаю, что начать будет лучше с обзора ситуации до начала повсеместной поддержки WebP.

Немного истории

Основными графическими форматами в вебе сейчас являются JPEG и PNG, как и на заре появления интернета. Тогда, правда, к ним можно было отнести еще и GIF, который сейчас уже практически не актуален. Всем этим форматам уже более 20 лет.

И тем не менее, оба формата неплохо себя зарекомендовали. JPEG, формат с потерями качества — для изображений фотографического типа. PNG, без потерь — для пиктограмм, иконок и изображений с альфа каналом (прозрачностью). Для обоих форматов есть способы уменьшить размер файла, например, использование современных кодеков для JPEG (Guetzli, MozJPEG), или использование меньшей разрядности (квантование) для PNG изображений с небольшим количеством цветов. Об этом уже написано сотни статей.

Но технологии шагнули далеко вперед, веб вышел на новый уровень, и графические форматы в том числе. Новые прогрессивные форматы также появлялись, но главной проблемой оставалась именно поддержка их браузерами. До недавнего времени…

Формат WebP

WebP был разработан в Google и представлен в 2010 году как универсальный формат для веб изображений с открытым исходным кодом. WebP может использоваться как с потерями качества, так и без, с поддержкой альфа канала для обоих случаев.

При этом в обоих вариантах размер файла в той или иной степени будет меньше по сравнению с JPEG и PNG. Это достигается за счет более современных алгоритмов кодирования и сжатия. В определенных случаях размер файла может быть снижен даже в 10 раз, но в среднем это будет порядка 30%.

То есть да, WebP действительно способен заменить растровые веб форматы и стать единым стандартом.

Приводить примеры изображений, чтоб вы могли попытаться увидеть разницу, я не буду. С высокими уровнями качества (70-99) вы ее все равно не увидите. Использование же совсем уж низкого качества ради экономии нескольких десятков байт — это довольно редкий случай, и мы не будем на него отвлекаться.

Сейчас WebP поддерживается основными браузерами: Chrome, Firefox, Edge. На момент написания статьи — это порядка 75%, актуальная информация тут.

Да, пока мы все же не можем полностью отказаться от JPEG и PNG, они понадобятся для браузеров без поддержки WebP, из актуальных это только Safari.

В графических редакторах нативная поддержка WebP есть в Sketch. Для Photoshop и Gimp существуют плагины от сторонних разработчиков.

По поводу Photoshop замечу, что плагин, который появляется на первой позиции в поиске у меня не заработал (пробовал на версиях CC 2018 и СС 2019 для macOS), но в любом случае, он выглядит довольно устаревшим и содержит ряд ограничений. Мне подошел менее известный плагин, хоть он и в стадии beta.

Еще полноценная поддержка WebP есть в кроссплатформенном просмотрщике/конвертере XnView MP — это удобный инструмент с графическим интерфейсом для пакетной работы с изображениями. Кстати, с его помощью можно производить и оптимизацию изображений, в том числе и в других форматах, программа очень мощная.

Сам же разработчик предоставляет консольные утилиты для работы с форматом WebP, это может быть полезно при работе без графического интерфейса, например, на сервере.

Команды для конвертации и оптимизации изображений

В рамках статьи команды для конвертации нам не понадобятся, но они могут иногда пригодиться в работе, поэтому пусть тоже будут:

Установка пакетов для конвертации и оптимизации в Ubuntu:

sudo apt install optipng libjpeg-turbo-progs imagemagick php-imagick webp

Установка в macOS c помощью Homebrew:

brew install optipng jpeg imagemagick webp

Конвертация из PNG в WebP (здесь и далее используется коэффициент качества 90 из 100):

cwebp -q 90 SOURCE.png -o RESULT.webp

Пакетная конвертация всех PNG файлов в папке в WebP:

for F in *.png ; do cwebp -q 90 $F -o `basename ${F%.png}`.webp ; done

Конвертация из PNG в JPEG:

convert SOURCE.png pnm:- | cjpeg -quality 90 > RESULT.jpg

Пакетная конвертация всех PNG файлов в папке в JPEG:

for F in *.png ; do convert $F pnm:- | cjpeg -quality 90 > `basename ${F%.png}`.jpg ; done

Пакетная оптимизация всех PNG файлов в папке:

find -name '*.png' -print0 | xargs -0 optipng -nc -nb -o7

Пакетная оптимизация всех JPEG файлов в папке:

find . -regex ".*\.\(jpeg\|jpg\)" -print0 | xargs -0 -I filename jpegtran -copy none -optimize -outfile filename filename

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

Использование изображений в качестве контента

Важно понимать, что изображения могут использоваться в разметке, тег img — для изображений, являющихся контентом, а также в CSS, свойство background-image — для изображений, являющихся оформлением.

Все дальнейшие изыскания связаны с тем, что нам нужно не просто указать изображение в формате WebP, а еще и предоставить запасной вариант (фолбэк) в виде JPEG/PNG изображения, если WebP не поддерживается браузером. В идеале было бы предоставлять запасные варианты стандартными средствами, но это не всегда возможно.

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

Изображения в разметке могут быть добавлены разработчиком/контент-менеджером сайта как в виде непосредственно самой разметки (в редакторе), так и с помощью полей, в которые могут загружаться файлы изображений. В последнем случае за формирование разметки будет отвечать один или несколько модулей Drupal. Эти варианты мы разберем поверхностно, ниже.

В стандарте HTML5 был представлен элемент picture, который может содержать пути к нескольким изображениям, и отображать нужное в зависимости от контекста. В основном это используется для показа нужного изображения в зависимости от ширины и плотности экрана. Но этот механизм может применяться и для показа WebP, если формат поддерживается браузером или показа JPEG/PNG, если не поддерживается.

Вариант использования элемента picture с двумя типами изображений в виде разметки:

<picture>
  <source type="image/webp" srcset="image.webp">
  <img src="image.jpg">
</picture>

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

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

Существует модуль WebP (на момент написания статьи в стадии beta), который может автоматически генерировать WebP версии загружаемых изображений, и с помощью файла конфигурации веб сервера Apache подменять пути к изображениям. Этот способ лично мне не подошел, так как я использую веб сервер Nginx. Да и просто я не уверен, что это правильный подход.

Но для модуля так же существует патч с попыткой реализации подхода с элементом picture на основе ядерного модуля Responsive Image. Ему я уделил самое пристальное внимание, но в моем случае он тоже не сработал. Изображения ведь могут использоваться в различных контекстах, в представлениях, в слайдере, в галерее, и пр., и, видимо, мне просто не повезло.

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

Использование изображений в теме оформления

Предположим, что в нашей теме оформления уже используются изображения в формате PNG и JPEG. Пока мы не будем отходить от этой практики, а просто предложим браузеру загрузить вместо этих изображений другие, в формате WebP, если он его поддерживает.

Объединить запись в одно свойство, как в случае с элементом picture, в CSS, к сожалению, не получится, но можно сделать, например, так:

.element-with-bg {
  background-image: url('image.jpg');
}

.webp .element-with-bg {
  background-image: url('image.webp');
}

Добавить класс webp к корневому элементу (тег html) может JS библиотека Modernizr, которая кстати присутствует в ядре Drupal 8, но в практически минимальном виде, без поддержки WebP.

Это можно исправить, скачав собственную сборку библиотеки на странице загрузки. Передя по этой ссылке, будет отмечен пункт "Webp" а так же пункты "Touch Events" и "details Element", которые как раз используются в ядре Drupal. В результате скачается файл modernizr-custom.js, который надо положить в папку js вашей темы оформления MYTHEME.

Теперь надо определить этот файл как библиотеку в файле MYTHEME.libraries.yml:

modernizr:
  js:
    js/modernizr-custom.js: { preprocess: 0, weight: -21 }

И переопределить аналогичный файл, который идет в составе ядра, в файле MYTHEME.info.yml:

libraries:
  — core/modernizr

libraries-override:
  core/modernizr: MYTHEME/modernizr

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

С этим хорошо справится сборщик проектов Gulp и его плагины. О Gulp я уже писал, и если вы его еще не используете, то самое время начать. Статья по ссылке уже немного устарела, вышел Gulp версии 4, его мы и будем использовать.

Для пакетной генерации WebP изображений есть плагин gulp-webp.

Для автоматического добавления класса webp к объявлениям картинок есть плагин gulp-webpcss.

Первым делом нужно будет установить пакеты для конвертации и оптимизации, команды я приводил выше.

Процесс установки Gulp и плагинов я опущу, но приведу фрагмент описания задачи в gulpfile.js:

var gulp = require('gulp');
var webp = require('gulp-webp');
var webpcss = require("gulp-webpcss");

var CSS = 'css';
var IMG = 'img';

gulp.task('webp', done => {
  gulp.src(IMG + '/src/*')
    .pipe(webp({quality: 90}))
    .pipe(gulp.dest(IMG));
  done();
});

gulp.task('sass', done => {
  gulp.src(SASS + '/**/*.scss')
    .pipe(webpcss({})
    .pipe(gulp.dest(CSS));
  done();
});

Добавление класса webp в моем примере происходит в рамках задачи по компиляции SASS, но фактически преобразования совершаются уже на скомпилированном CSS, так что в данном случае это не принципиально.

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

По умолчанию, плагин gulp-webpcss генерирует правила подобные тому, что приведено в качестве примера выше. И получается, что до отработки JavaScript мы имеем объявления с ненужными изображениями, даже если браузер поддерживает WebP. Думаю, что в теории возможна ситуация, когда ненужное изображение даже начнет загружаться. Так мы ничего не оптимизируем. Нужно действовать в интересах прогресса и большинства. Настройки gulp-webpcss позволяют переопределить классы как для WebP объявлений, так и для исходных:

gulp.task('sass', done => {
  gulp.src(SASS + '/**/*.scss')
    .pipe(webpcss({ webpClass: '', noWebpClass: '.no-webp' })
    .pipe(gulp.dest(CSS));
  done();
});

Это даст нам в итоге CSS вида:

.no-webp .element-with-bg {
  background-image: url('image.jpg');
}

.element-with-bg {
  background-image: url('image.webp');
}

Класс no-webp будет добавлен библиотекой Modernizr, если формат WebP не поддерживается. И теперь по умолчанию будут загружаться именно WebP изображения.

Причем остальные свойства сохранят исходную специфичность, то есть плагин делает свою работу с умом. Правда есть одна особенность CSS, которая все же не учитывается. Если свойство определено в сокращенном виде (как background), то повторное определение обнулит объединенные значения (background-color, background-repeat, background-size и прочие). При использование отдельных свойств такой проблемы не будет.

Но и это решение можно улучшить. Хоть JPEG и PNG и отошли на второй план, почему бы их все равно не оптимизировать? Для этого есть плагин gulp-imagemin. Плагин использует под капотом уже упомянутые выше optipng и jpegtran. Все шикарно работает даже с настройками по умолчанию:

var imagemin = require('gulp-imagemin');

gulp.task('imagemin', done => {
  gulp.src(IMG + '/src/*')
    .pipe(imagemin())
    .pipe(gulp.dest(IMG));
  done();
});

И раз уж мы используем Gulp, то поручим ему и всю остальную рутинную работу: сборку SASS c составлением карт (sourcemaps), расстановку браузерных префиксов и отслеживание изменений в файлах для запуска задач.

Полный код файла gulpfile.js приводить, пожалуй, не буду, но зато, я подготовил стартовый шаблон темы оформления со всеми необходимыми файлами, описанными выше. Я назвал его Dr Webper.

Webper 8.x (ZIP, 506 Кб)

Для его использования надо иметь установленный node.js и Gulp. Сначала надо скачать все зависимости выполнив команду:

npm install

Файлы изображений нужно поместить в папку img/src либо отредактировать путь в файле gulpfile.js.

Запуск сборки выполняется командой:

gulp build

Запуск отслеживания изменений:

gulp watch

или просто:

gulp

Мысли на тему оптимизации изображений

WebP хороший формат, но и его надо использовать по назначению. Для иконок, логотипов и подобных им простых изображений лучше использовать SVG. Это векторный формат, с отличной поддержкой браузерами, и при правильном использовании не требует оптимизации или сжатия. Формат имеет текстовую структуру XML и иногда может содержать служебные данные графических редакторов, но обычно они исключаются при экспорте. Сжатие же может производить веб сервер при отправке файла.

Второй важный момент — это использование размеров изображений, соответствующих отображаемым. Чтобы устройствам с маленькими экранами не приходилось загружать большие изображения, которые потом отображаются в уменьшенном виде. Даже с учетом двойной или даже тройной плотности пикселей на мобильных устройствах, изображения должны предоставляться в нескольких разрешениях, каждое в соответствующем медиа запросе. Это актуально больше для контентных изображений и может быть эффективно автоматизированно на стороне Drupal c помощью модуля Responsive Image. Но пренебрегать этими принципами в теме оформления тоже не стоит.

Если вы использовали PageSpeed Insights для оценки производительности сайта, то наверняка замечали, что он постоянно жалуется на недостаточную оптимизацию изображений. Это могло происходить даже с оптимизированными изображениями, потому что сервис очень строг к файлам JPEG и PNG. C WebP этого скорее всего не будет. Но не радуйтесь раньше времени, PageSpeed всегда найдет к чему придраться :)

Итоги

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

Автоматизировать генерацию и объявление WebP изображений в теме оформления довольно просто. При этом вам даже не понадобится менять свой привычный способ написания стилей. Сборщик все сделает сам. Использование WebP можно добавить как в существующие темы оформления, так и начать использовать при разработке новой.

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