Новые возможности в Chrome 65
CSS Paint API (также известный как «CSS Custom Paint» или «Houdini's paint worklet») включен по умолчанию, начиная с Chrome 65. Что это? Что вы можете с ним сделать? И как он работает? Ну что ж, читайте дальше, ладно?
CSS Paint API позволяет программно генерировать изображение всякий раз, когда свойство CSS ожидает изображение. Такие свойства, как background-image
или border-image
, обычно используются с url()
для загрузки файла изображения или со встроенными функциями CSS, такими как linear-gradient()
. Вместо того, чтобы использовать их, теперь вы можете использовать paint(myPainter)
для ссылки на worklet paint .
Написание рабочей тетради по покраске
Чтобы определить ворклет рисования с именем myPainter
, нам нужно загрузить файл ворклета рисования CSS с помощью CSS.paintWorklet.addModule('my-paint-worklet.js')
. В этом файле мы можем использовать функцию registerPaint
для регистрации класса ворклета рисования:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
Внутри обратного вызова paint()
мы можем использовать ctx
так же, как мы использовали бы CanvasRenderingContext2D
, как мы знаем его из <canvas>
. Если вы знаете, как рисовать в <canvas>
, вы можете рисовать в ворклете paint! geometry
сообщает нам ширину и высоту холста, который находится в нашем распоряжении. properties
я объясню позже в этой статье.
В качестве вводного примера давайте напишем ворклет с шахматной доской и используем его в качестве фонового изображения <textarea>
. (Я использую textarea, потому что ее размер по умолчанию можно изменять.)
<!-- index.html -->
<!doctype html>
<style>
textarea {
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
paint(ctx, geom, properties) {
// Use `ctx` as if it was a normal canvas
const colors = ['red', 'green', 'blue'];
const size = 32;
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
const color = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(x * size, y * size, size, size);
ctx.fill();
}
}
}
}
// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);
Если вы уже использовали <canvas>
в прошлом, этот код должен показаться вам знакомым. Посмотрите живую демонстрацию здесь.

Отличие от использования обычного фонового изображения здесь в том, что узор будет перерисовываться по требованию, всякий раз, когда пользователь изменяет размер текстовой области. Это означает, что фоновое изображение всегда имеет именно такой размер, какой ему нужно, включая компенсацию для дисплеев с высокой плотностью.
Это довольно круто, но это также довольно статично. Хотели бы мы писать новый ворклет каждый раз, когда нам нужен тот же узор, но с квадратами разного размера? Ответ — нет!
Параметризация вашего ворклета
К счастью, ворклет paint может получить доступ к другим свойствам CSS, и именно здесь в игру вступают дополнительные properties
параметров. Присвоив классу статический атрибут inputProperties
, вы можете подписаться на изменения любого свойства CSS, включая пользовательские свойства. Значения будут предоставлены вам через параметр properties
.
<!-- index.html -->
<!doctype html>
<style>
textarea {
/* The paint worklet subscribes to changes of these custom properties. */
--checkerboard-spacing: 10;
--checkerboard-size: 32;
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
// inputProperties returns a list of CSS properties that this paint function gets access to
static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }
paint(ctx, geom, properties) {
// Paint worklet uses CSS Typed OM to model the input values.
// As of now, they are mostly wrappers around strings,
// but will be augmented to hold more accessible data over time.
const size = parseInt(properties.get('--checkerboard-size').toString());
const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
const colors = ['red', 'green', 'blue'];
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
ctx.fillStyle = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);
Теперь мы можем использовать один и тот же код для всех видов шахматных досок. Но что еще лучше, теперь мы можем зайти в DevTools и поиграться со значениями, пока не найдем правильный вид.
Браузеры, не поддерживающие Paint Worklet
На момент написания статьи только Chrome реализовал paint worklet. Хотя есть положительные сигналы от всех других поставщиков браузеров, особого прогресса нет. Чтобы быть в курсе событий, регулярно проверяйте Is Houdini Ready Yet?. В то же время обязательно используйте прогрессивное улучшение, чтобы ваш код работал, даже если поддержка paint worklet отсутствует. Чтобы убедиться, что все работает так, как и ожидалось, вам нужно настроить свой код в двух местах: CSS и JS.
Поддержку paint worklet в JS можно определить, проверив объект CSS
: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); }
Для стороны CSS у вас есть два варианта. Вы можете использовать @supports
:
@supports (background: paint(id)) {
/* ... */
}
Более компактный трюк — использовать тот факт, что CSS делает недействительным и впоследствии игнорирует все объявление свойства, если в нем есть неизвестная функция. Если вы указываете свойство дважды — сначала без paint worklet, а затем с paint worklet — вы получаете прогрессивное улучшение:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
В браузерах с поддержкой paint worklet второе объявление background-image
перезапишет первое. В браузерах без поддержки paint worklet второе объявление недействительно и будет отброшено, оставив в силе первое объявление.
CSS Paint Полифилл
Во многих случаях можно также использовать CSS Paint Polyfill , который добавляет поддержку CSS Custom Paint и Paint Worklets в современные браузеры.
Варианты использования
Существует множество вариантов использования paint worklet, некоторые из них более очевидны, чем другие. Одним из наиболее очевидных является использование paint worklet для уменьшения размера вашего DOM. Часто элементы добавляются исключительно для создания украшений с помощью CSS. Например, в Material Design Lite кнопка с эффектом ряби содержит 2 дополнительных элемента <span>
для реализации самой ряби. Если у вас много кнопок, это может привести к значительному количеству элементов DOM и может привести к снижению производительности на мобильных устройствах. Если вы реализуете эффект ряби с помощью paint worklet , вы получите 0 дополнительных элементов и только один paint worklet. Кроме того, у вас есть то, что гораздо проще настраивать и параметризировать.
Еще одним преимуществом использования paint worklet является то, что — в большинстве сценариев — решение с использованием paint worklet невелико в байтах. Конечно, есть компромисс: ваш код paint будет запускаться всякий раз, когда изменяется размер холста или любой из параметров. Поэтому, если ваш код сложный и выполняется долго, это может привести к подтормаживанию. Chrome работает над тем, чтобы вывести paint worklets из основного потока, чтобы даже долго работающие paint worklets не влияли на отзывчивость основного потока.
Для меня самая захватывающая перспектива заключается в том, что paint worklet позволяет эффективное полифилирование функций CSS, которых пока нет в браузере. Одним из примеров может быть полифилирование конических градиентов , пока они не появятся в Chrome изначально. Другой пример: на встрече CSS было решено, что теперь можно иметь несколько цветов границ. Пока эта встреча еще продолжалась, мой коллега Ян Килпатрик написал полифил для этого нового поведения CSS с использованием paint worklet.
Мыслить нестандартно
Большинство людей начинают думать о фоновых изображениях и изображениях границ, когда узнают о paint worklet. Менее интуитивный вариант использования paint worklet — это mask-image
чтобы элементы DOM имели произвольные формы. Например, ромб :

mask-image
принимает изображение, которое имеет размер элемента. Области, где изображение маски прозрачно, элемент прозрачен. Области, где изображение маски непрозрачно, элемент непрозрачен.
Теперь в Chrome
Paint Worklet уже некоторое время находится в Chrome Canary. В Chrome 65 он включен по умолчанию. Попробуйте новые возможности, которые открывает paint Worklet, и покажите нам, что вы создали! Для большего вдохновения взгляните на коллекцию Винсента Де Оливейры .