Создание адаптивных изображений при рендеринге Markdown в Eleventy

Для чего нужны адаптивные изображения

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

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

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

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

Для этого, сначала устанавливаем плагин, выполнив команду из корня проекта:

# terminal
npm install @11ty/eleventy-img

Затем, в конфигурационном файле Eleventy, подключаем плагин:

// .eleventy.js
...
const img = require("@11ty/eleventy-img");
...

Далее, в конфигурационном файле Eleventy, добавляем функцию-обработчик шорткода, генерирующую адаптивный HTML-код и изображения в различных разрешениях:

// .eleventy.js
...
function imgShortcode(src, alt, cls, sizes) {
  const options = {
    widths: [460, 620, 780, 940, 1100],
    formats: ["webp", "jpeg"]
  };
  img(src, options);
  const attributes = {
    alt: alt,
    class: cls,
    sizes: sizes,
  };
  const metadata = img.statsSync(src, options);
  return img.generateHTML(metadata, attributes);
}
...

В параметре widths указывается массив размеров изображений (по ширине), которые должен сформировать плагин из исходного изображения (высота изображений будет пропорциональной), а в параметре formats - массив форматов генерируемых изображений. Всего, в этом примере, плагин создаст 10 вариантов изображений - 5 размеров в формате webp и 5 размеров в формате jpeg.

Чтобы можно было использовать подготовленный шорткод в шаблонах Nunjucks, включаем его в конфигурацию Eleventy:

// .eleventy.js
...
module.exports = (config) => {
  ...
  config.addNunjucksShortcode("img", imgShortcode);
  ...
};

Теперь, в шаблонах Nunjucks, можно использовать этот шорткод следующим образом:

// page.njk
{% img "path/image.png", "Аlt name", "class1 class2", "70vw" %}

В шорткод передаётся 4 параметра:

  1. наименование исходного изображения и путь к нему,
  2. альтернативное наименование изображения (атрибут alt),
  3. классы стилей изображения (атрибут class),
  4. ширина изображения при различных разрешениях экрана (атрибут sizes).

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

На выходе получим примерно такой HTML-код:

<!-- page.html -->
<img
  alt="Аlt name"
  class="class1 class2"
  src="/images/JBdBZcXjYP.jpeg"
  width="1100"
  height="576"
  srcset="
    /images/JBdBZcXjYP-460.webp   460w,
    /images/JBdBZcXjYP-620.webp   620w,
    /images/JBdBZcXjYP-780.webp   780w,
    /images/JBdBZcXjYP-940.webp   940w,
    /images/JBdBZcXjYP-1100.webp 1100w
  "
  sizes="70vw" />

С адаптивными изображениями в шаблонах разобрались, но с изображениями в Markdown не все так просто.

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

Когда редактор сайта вставляет изображение в текст страницы, Markdown создаёт следующий код:

<!-- post.md -->

![Alt name](path/image.png)

Этот код, при рендеринге Markdown, преобразуется в простой (не адаптивный) HTML-тег:

<!-- post.html -->
<img alt="Аlt name" src="/images/image.png" />

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

Для этого, в конфигурационном файле Eleventy, создаём свой экземпляр объекта markdown-it с необходимыми свойствами:

// .eleventy.js
...
const markdown = require("markdown-it")({
  html: true,
  breaks: true,
  linkify: true,
});
...

Далее переопределяем метод renderer.rules.image созданного объекта markdown для обработки изображений с помощью нашего шорткода:

// .eleventy.js
...
markdown.renderer.rules.image = (tokens, idx) => {
  const token = tokens[idx];
  return imgShortcode(
    token.attrGet("src"),
    token.content,
    "class1 class2",
    "70vw"
  );
};
...

И последнее - в конфигурации Eleventy заменяем системный объект markdown-it на вновь созданный с переопределённым методом рендеринга изображений:

// .eleventy.js
...
module.exports = (config) => {
  ...
  config.setLibrary("md", markdown);
  ...
};

Результирующий код будет примерно таким:

// .eleventy.js
...
const img = require("@11ty/eleventy-img");
function imgShortcode(src, alt, cls, sizes) {
  const options = {
    widths: [460, 620, 780, 940, 1100],
    formats: ["webp", "jpeg"]
  };
  img(src, options);
  const attributes = {
    alt: alt,
    class: cls,
    sizes: sizes,
  };
  const metadata = img.statsSync(src, options);
  return img.generateHTML(metadata, attributes);
}
...
const markdown = require("markdown-it")({
  html: true,
  breaks: true,
  linkify: true,
});
markdown.renderer.rules.image = (tokens, idx) => {
  const token = tokens[idx];
  return imgShortcode(
    token.attrGet("src"),
    token.content,
    "class1 class2",
    "70vw"
  );
};
...
module.exports = (config) => {
  ...
  config.addNunjucksShortcode("img", imgShortcode);
  config.setLibrary("md", markdown);
  ...
};

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

AG & Dev © 2020 Moscow