API рисования CSS

Новые возможности в Chrome 65

CSS Paint API (также известный как «CSS Custom Paint» или «Рабочая программа рисования Houdini») включен по умолчанию, начиная с Chrome 65. Что это такое? Что вы можете с этим сделать? И как это работает? Ну, читайте дальше, ладно…

CSS Paint API позволяет программно генерировать изображение всякий раз, когда свойство CSS ожидает изображение. Такие свойства, как background-image или border-image обычно используются с url() для загрузки файла изображения или со встроенными функциями CSS, такими как linear-gradient() . Вместо их использования теперь вы можете использовать paint(myPainter) для ссылки на рабочий файл рисования .

Написание программы рисования

Чтобы определить рабочую программу рисования под названием 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> . (Я использую текстовую область, потому что по умолчанию ее размер можно изменить.):

<!-- 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> , этот код должен показаться вам знакомым. Смотрите живую демонстрацию здесь.

Текстовая область с шахматным узором в качестве фонового изображения
Текстовая область с шахматным узором в качестве фонового изображения.

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

Это довольно круто, но это также довольно статично. Захотим ли мы писать новый ворлет каждый раз, когда нам нужен тот же узор, но с квадратами разного размера? Ответ — нет!

Параметризация вашего рабочего листа

К счастью, инструмент рисования может получить доступ к другим свойствам 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 и поиграть со значениями, пока не найдем правильный вид.

Браузеры, которые не поддерживают работу с краской

На момент написания только Chrome реализовал работу с краской. Хотя есть положительные сигналы от всех других поставщиков браузеров, особого прогресса не наблюдается. Чтобы быть в курсе событий, проверьте «Готов ли Гудини?» регулярно. А пока обязательно используйте прогрессивное улучшение, чтобы ваш код продолжал работать, даже если нет поддержки рисования. Чтобы убедиться, что все работает так, как ожидалось, вам необходимо настроить свой код в двух местах: CSS и JS.

Обнаружить поддержку рисования в JS можно, проверив объект CSS : js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } Что касается CSS, у вас есть два варианта. Вы можете использовать @supports :

@supports (background: paint(id)) {
  /* ... */
}

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

textarea {
  background-image: linear-gradient(0, red, blue);
  background-image: paint(myGradient, red, blue);
}

В браузерах с поддержкой рисования второе объявление background-image перезапишет первое. В браузерах без поддержки рисования второе объявление недействительно и будет отброшено, оставив в силе первое объявление.

CSS Paint Полифилл

Для многих целей также можно использовать CSS Paint Polyfill , который добавляет поддержку CSS Custom Paint и Paint Worklets в современные браузеры.

Варианты использования

Существует множество вариантов использования программ рисования, некоторые из них более очевидны, чем другие. Один из наиболее очевидных — использование Paint Worklet для уменьшения размера DOM. Часто элементы добавляются исключительно для украшения с помощью CSS. Например, в Material Design Lite кнопка с эффектом пульсации содержит 2 дополнительных элемента <span> для реализации самой пульсации. Если у вас много кнопок, это может привести к увеличению количества элементов DOM и привести к снижению производительности на мобильных устройствах. Если вместо этого вы реализуете эффект ряби с помощью рисования , у вас останется 0 дополнительных элементов и только один рисование. Кроме того, у вас есть что-то, что гораздо проще настроить и параметризовать.

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

На мой взгляд, самая захватывающая перспектива заключается в том, что работа с краской позволяет эффективно использовать функции CSS, которых пока нет в браузере. Одним из примеров может быть полифиллинг конических градиентов до тех пор, пока они не появятся в Chrome изначально. Другой пример: на собрании CSS было решено, что теперь вы можете использовать несколько цветов границ. Пока эта встреча еще продолжалась, мой коллега Ян Килпатрик написал полифилл для этого нового поведения CSS, используя ворлет рисования.

Мышление вне «коробки»

Большинство людей начинают думать о фоновых изображениях и изображениях границ, когда узнают о работе с краской. Еще один менее интуитивно понятный вариант использования рисования — это mask-image , позволяющая придать элементам DOM произвольную форму. Например, бриллиант :

DOM-элемент в форме ромба.
DOM-элемент в форме ромба.

mask-image принимает изображение размером с элемент. Области, где изображение маски прозрачно, элемент является прозрачным. Области, где изображение маски непрозрачно, элемент непрозрачен.

Теперь в Chrome

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