Несколько лет назад ходили слухи о появлении в CSS спецификации под названием Houdini. Впервые описанный как способ написания полифиллов для CSS, Houdini - это набор API, который дает разработчикам программный контроль над макетом, рисованием, анимацией и многим другим.
В этой статье мы рассмотрим CSS Paint API, спецификацию Houdini, которая позволяет вам определять новое поведение рисования. Мы также быстро рассмотрим новую типизированную объектную модель CSS (или типизированную CSS-модель), также являющуюся частью Houdini, которую можно использовать с Custom Properties с большим эффектом.
Прежде чем мы перейдем к этому, я должен упомянуть поддержку браузера. На момент написания Chrome - единственный браузер, поддерживающий эти технологии, однако есть полифилы для CSS Paint API и CSS Typed OM. Ура! Итак, что же они такие?
CSS Paint API
CSS Paint API - это новый захватывающий набор, который дает вам возможность использовать CanvasRenderingContext2D там, где вы обычно используете изображение в CSS. По сути, он превращает background-image
любого элемента в <canvas>
.
Исходный пост Google Developers Houdini от 2016 года немного не синхронизирован со спецификацией, например, API переместились под глобальный объект window.CSS
, но основные концепции остались прежними. Как и другие API-интерфейсы Houdini, Paint API основан на концепции рабочихлетов.
Worklets - это небольшие скрипты, похожие на Workers, но с некоторыми отличиями. Если рабочие процессы долговечны и предназначены для выполнения дорогостоящей в вычислительном отношении работы вне основного потока, рабочиелеты целенаправленно легковесны для выполнения более мелких операций несколько раз за кадр, в отличие от шейдеров.
Вот очень простой пример, в котором мы используем программу рисования, чтобы закрасить фон элемента в синий цвет:
<!DOCTYPE html> <style> .hello-world { background-image: paint(hello-world); height: 200px; width: 300px; } </style> <div class="hello-world"></div> <script> CSS.paintWorklet.addModule(’hello-world.js’); </script>
И в hello-world.js
(наш рабочий лист):
class HelloWorld { paint(context, geometry) { context.fillStyle = 'blue'; context.fillRect(0, 0, geometry.width, geometry.height); } } registerPaint('hello-world', HelloWorld);
Обратите внимание, что мы определяем класс с помощью одного метода paint()
. Первый аргумент, переданный ему, представляет собой двумерный контекст, подобный холсту, а второй дает геометрию окрашиваемого элемента. Метод paint()
будет запускаться каждый раз при изменении геометрии, чаще всего при изменении размера.
Давайте сделаем это немного интереснее, добавив некоторую поддержку тем с помощью Custom Properties и CSS Typed OM.
Типизированная объектная модель CSS
Некоторое время не хватало интерфейса между CSS и JavaScript. CSS имеет систему сложных типов, которая может представлять значения от длины, угла, продолжительности и частоты до таких функций, как цвета, формы, тайминги и преобразования, но, несмотря на это, основной способ взаимодействия с этими значениями в JavaScript - это передача и анализ струны.
CSS Typed OM направлен на то, чтобы исправить это, предоставляя хуки для создания и управления значениями CSS в JavaScript. Значения могут быть созданы с помощью методов в window.CSS
global, например CSS.px(20)
возвращает CSSUnitValue - объект со свойством unit
и value
, который при приведении к строке дает "20px"
. Модель также дает каждому элементу свойство attributeStyleMap
и метод computedStyleMap()
, которые принимают эти новые типы:
const div = document.createElement('div'); div.attributeStyleMap.set('margin-top', CSS.px(20));
Точно так же вы можете get
такие свойства:
const property = div.attributeStyleMap.get(’margin-top’); property.value === 20 // true property.unit === 'px' // true
Типизированная OM - это гораздо больше, чем этот простой пример - я настоятельно рекомендую прочитать Работа с новой типизированной объектной моделью CSS на сайте Google Developers, но действительно крутая вещь, которую он дает вам, - это возможность регистрировать определения для новых свойств.
Давайте определим два новых свойства для нашего hello-world
примера, используя новый метод CSS.registerProperty()
, --background-color
и --background-padding
, которые мы зарегистрируем как типы <color>
и <length>
соответственно.
<!DOCTYPE html> <style> .hello-world { --background-color: red; --background-padding: 1em; background-image: paint(hello-world); border: 1px solid black; height: 200px; width: 300px; } </style> <div class="hello-world"></div> <script> CSS.registerProperty({ name: '--background-color', syntax: '<color>', initialValue: 'blue', inherits: false, }); CSS.registerProperty({ name: '--background-padding', syntax: '<length>', initialValue: 0, inherits: false, }); CSS.paintWorklet.addModule('hello-world.js'); </script>
Вы можете передать registerProperty()
несколько параметров. Вы можете добавить initialValue
, чтобы указать значение по умолчанию в случае, если свойство отсутствует (или недействительно), а логическое значение inherits
сообщает синтаксическому анализатору, унаследованы ли значения от родительского элемента. Ключ syntax
, однако, является наиболее сложным и используется для того, чтобы сообщить парсеру, какие типы CSS следует принимать как допустимые - полный список поддерживаемых строк синтаксиса см. В спецификации.
В нашем рабочем листе нам нужно внести некоторые изменения, чтобы Paint API знал, что мы хотим использовать наши настраиваемые свойства в качестве входных данных. Затем они становятся доступными для paint()
метода в качестве третьего аргумента.
class HelloWorld { static get inputProperties() { return [ '--background-color', '--background-padding', ]; } paint(context, geometry, properties) { const color = properties.get('--background-color').value; const padding = properties.get('--background-padding').value; const height = geometry.height - padding * 2; const width = geometry.width - padding * 2; context.fillStyle = color; context.fillRect(padding, padding, width, height); } } registerPaint('hello-world', HelloWorld);
Обратите внимание, что даже несмотря на то, что в --background-padding
указывается в em
единицах, типизированный OM по умолчанию преобразует это в px
, поэтому мы можем использовать его непосредственно в нашем контексте рисования без необходимости преобразовывать его самостоятельно. Если вы используете Chrome, вы можете увидеть окончательный результат здесь:
Попробуйте поиграть со значениями --background-color
и --background-padding
в DevTools и обратите внимание, что изменения отражаются немедленно, как «настоящие» свойства CSS.
Заключение
Для Houdini это только начало. Помимо рисования, в разработке находятся API-интерфейсы для макета и анимации, а в API веб-аудио были включены рабочие листы.
Демонстрационные примеры в этой статье касаются только возможностей Paint API и Typed OM. Хотя пока они реализованы только в одном браузере, я рад возможности использовать эти функции для постепенного улучшения. А пока вы можете отслеживать принятие браузерами других производителей на сайте ishoudinireadyyet.com.