Изучение нового CSS Houdini Painting API

Изучение нового CSS Houdini Painting API

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

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

Как я уже упоминал в своей недавней статье об анимации холста внутри компонентов React, мне нравится HTML-холст. Поэтому я был очень взволнован, узнав о новых API-интерфейсах CSS Houdini, когда читал короткую статью Стивена Фулхэма об этом на css-tricks.com.

Основная причина моего восхищения заключается в том, что API рисования позволяет создавать нативные изображения CSS путем рисования в PaintRenderingContext2D (который в значительной степени является точной копией 2D-контекста, в котором мы рисуем, когда используем обычный API Canvas, за исключением небольшого набора функций).

Используя API рисования, мы можем программно рисовать изображения и использовать их в CSS. При рисовании мы можем получать параметры с информацией, поступающей из DOM и применяемых таблиц стилей.

Веб-документация MDN описывают это так: Houdini — это набор низкоуровневых API-интерфейсов, которые раскрывают части движка CSS, давая разработчикам возможность расширять CSS, подключаясь к процессу стилизации и компоновки движка рендеринга браузера.

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

В этой статье мы рассмотрим CSS Painting API (и Worklets).

Важное примечание: CSS Houdini в целом все еще является экспериментальной технологией. Но, как упоминалось ранее, большинство браузеров реализуют его или серьезно рассматривают возможность его реализации. Google Chrome — поддерживает Painting API начиная с версии 65, так что именно этот браузер мы будем использовать сегодня.

CSS Painting API

Используя этот API, мы можем программно рисовать изображения и использовать эти изображения в CSS. Вот что мы создадим:

Изучение нового CSS Houdini Painting API

Как ни странно, это три элемента DIV и только три элемента DIV. Об этом свидетельствует файл index.html:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="utf-8" /> <title>CSS Painting API example</title> <script src="main.js"></script> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body> <div class="pane"> Lorem ipsum... </div> <div class="pane large"> Lorem ipsum... </div> <div class="pane mini"> Lorem ipsum... </div> </body>
</html>

«Стилизация» (рисование…) фона трех панелей выполняется с помощью API рисования программно. Все три панели отрисовываются одной и той же функцией. Давайте посмотрим на класс CSS pane в таблице стилей:

.pane { background-image: paint(pane); font-size: 1em; --dot-spacing: 5px;
} .pane.mini { font-size: 12px; --pane-color: #7470b5;
} .pane.large { font-size: 24px; --pane-color: #b068bd;
}

Мы рисуем что-то, называемое панелью, в строке 2. Это требует объяснения, и мы скоро его рассмотрим.

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

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

Помимо этого, стоит упомянуть, что все панели имеют разный размер шрифта (имейте это в виду, мы еще вернемся к этому позже). И мы видим, что используются две пользовательские переменные CSS: ‑‑dot‑spacing и ‑‑pane‑color.

В переменных CSS нет ничего нового. Они существуют с 2014/2016 (соответственно в Firefox / Google Chrome). Но один из новых API-интерфейсов CSS Houdini, называемый API свойств и значений CSS, позволяет нам регистрировать эти пользовательские переменные, чтобы браузер знал о них больше, что пригодится.

Внутри таблицы стилей мы можем зарегистрировать их следующим образом:

@property --pane-color { syntax: "<color>"; inherits: true; initial-value: #646464;
} @property --dot-spacing { syntax: "<length>"; inherits: true; initial-value: 5px;
} .pane { /* etc. */
}

Обратите внимание, что мы также могли сделать это в JavaScript. Результат будет таким же.

Так зачем мы это делаем? Теперь браузер и его движок визуализации знают подробности об этих свойствах. Он знает, что pane color содержит значение цвета и что значение по умолчанию — «#646464». И dot spacing содержит значение длины, по умолчанию «5 пикселей».

Две новые переменные теперь называются зарегистрированными пользовательскими переменными. Вернемся к этой строке:

background-image: paint(pane);

API рисования позволяет рисовать изображения. Функция paint получает один параметр. Этот параметр представляет собой класс JavaScript, зарегистрированный как Paint в файле с именем worklet.js:

class Pane { // ...
} registerPaint("pane", Pane);

Но! Этот код не может быть выполнен в обычной среде выполнения JavaScript. Он должен быть выполнен внутри так называемого — отсюда и название файла — Worklet:

«Интерфейс Worklet — это облегченная версия Web Workers, которая дает разработчикам доступ к низкоуровневым частям конвейера рендеринга. С помощью Worklets вы можете запускать код JavaScript и WebAssembly для рендеринга графики или обработки звука там, где требуется высокая производительность».

Мы можем убедиться, что Worklet будет выполнен, добавив следующую строку в обычный файл JavaScript (main.js), который загружается внутри index.html, например:

// main.js
CSS.paintWorklet.addModule("worklet.js");

Если этот вызов возвращает ошибку, ваш браузер не поддерживает Painting API. Теперь класс Paint «Pane» зарегистрирован под именем «pane», и мы можем использовать его в CSS, как мы видели ранее:

background-image: paint(pane);

Подробная информация о панели класса Paint

Давайте рассмотрим детали класса Paint внутри worklet.js:

class Pane { static get inputProperties() { return ["font-size", "--pane-color", "--dot-spacing"]; } static get contextOptions() { return { alpha: true }; } paint(ctx, size, styleMap) { // ... }
}

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

Возвращаемое значение contextOptions указывает, что мы хотим использовать прозрачность в нашем холсте.

И, наконец, в строке 10 функция paint. В этой функции мы сделаем фактический рисунок. В нашем случае он получает три параметра:

ctx: 2D-контекст нашего холста. Это должно быть вам понятно, если вы знакомы с обычным элементом HTML Canvas.

size: экземпляр PaintSize с двумя свойствами: .width и .height. Это вычисленные размеры элемента HTML, для которого мы рисуем изображение. Включая отступы, если они установлены.

styleMap: доступное только для чтения представление блока (источника) объявления CSS. Это экземпляр StylePropertyMapReadOnly и содержит только значения для свойств, которые мы определили внутри статической функции inputProperties.

Для получения дополнительных сведений о последнем параметре вы можете прочитать об API типизированной объектной модели CSS и / или об интерфейсе StylePropertyMapReadOnly.

Совет: вы можете получить полный StylePropertyMapReadOnly (содержащий все вычисленные стили CSS) для любого элемента HTML в DOM, вызвав computedStyleMap в браузерах, которые его поддерживают:

const styleMap = document.getElementById('myElement').computedStyleMap();

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

class Pane { static get inputProperties() { return ["font-size", "--pane-color", "--dot-spacing"]; } paint(ctx, size, styleMap) { let item; // font-size is a CSS property with a syntax "<length>" value. // Therefor styleMap.get() will return a `CSSUnitValue` object. // It has two properties: .value, and .unit item = styleMap.get("font-size"); console.log(item.value); // output: 12 console.log(item.unit); // output: "px" console.log(item.toString()); // output: "12px" // --pane-color, one of our custom properties, // is a CSS property with a syntax "<color>" value. // We defined this in our stylesheet when we registered the property. // Therefor styleMap.get() will return a `CSSStyleValue` object. // It has no public properties, but we can cast it to a string. item = styleMap.get("--pane-color"); console.log(item.toString()); // output: "rgb(100, 100, 100)" }
}

Рисование самого изображения

Вернемся к логике нашей маленькой демонстрации. Единственное, что осталось, — это содержимое тела функции рисования.

Мы могли бы начать с простого рисования заполненного прямоугольника в качестве фонового изображения (вы должны распознать команды рисования на обычном холсте HTML):

class Pane { // ... paint(ctx, size, styleMap) { const paneColor = String(styleMap.get("--pane-color")); ctx.fillStyle = paneColor; ctx.fillRect(0, 0, size.width, size.height); }
}

… Который будет выглядеть так:

Изучение нового CSS Houdini Painting API

Нарисованные фоновые изображения!

Обратите внимание, как мы использовали пользовательское свойство CSS pane color для динамической установки стиля заливки.

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

Изучение нового CSS Houdini Painting API

Если мы обновим функцию рисования с помощью более причудливой логики (вы можете посмотреть подробности в файле worklet.js в репозитории), фоновое изображение будет выглядеть так:

Изучение нового CSS Houdini Painting API

CSS переходы

Обратите внимание на последовательность точек в правом нижнем углу. Расстояние между этими точками определяется значением пользовательского свойства CSS dot spacing, которое по умолчанию имеет значение «5 пикселей».

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

@property --dot-spacing { syntax: "<length>"; inherits: true; initial-value: 5px;
} .pane { /* ... */ --dot-spacing: 5px; transition: --dot-spacing 0.5s;
} .pane:hover { --dot-spacing: 15px;
}

… Что приводит к плавной анимации при наведении курсора на элементы:

Изучение нового CSS Houdini Painting API

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

Совет: мы также могли бы добавить бесконечную анимацию CSS для одного из пользовательских свойств CSS, что привело бы к созданию настраиваемого нарисованного анимированного фона!

Заключение

Я нахожу Houdini Painting API не только интересным, но и простым для понимания и мощным (производительным). Я полон идей по использованию этой новой функции, но мне нужно набраться терпения и подождать, пока все основные браузеры не начнут поддерживать ее, прежде чем мы сможем рассмотреть возможность ее использования в производственной среде. Спасибо за внимание!

Автор: Gerard van der Put

Источник: medium.com

Редакция: Команда webformyself.