CSS Painting API の高速入門です⚡

CSS Painting API は CSS Houdini( CSS の機能拡張)の API のひとつです。
JavaScript を用いて、Canvas の代わりに background-image に描画することができます。

とにかく動かす⚡

👇一番小さくやる例です。bodybackground-image を JavaScript で描いています。

// fill-blue-worklet.js
registerPaint('fill-black', class {
  paint(context) { context.fillRect(0, 0, 500, 500) }
})
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>CSS Painting API test</title>
  <script>CSS.paintWorklet.addModule('fill-black-worklet.js')</script>
  <style>
    .paint-target {
      width: 100vw;
      height: 100vh;
      background-image: paint(fill-black);
    }
  </style>
</head>
<body>
  <div class="paint-target"></div>
</body>
</html>

fill-black

はい!CSS Painting API の動作が確認できました。とてもシンプル。
(ローカルで動かす場合は CORS によるエラーを回避するために http-server を使います)

構成

Painting API(というか Houdini )は Worklet という独立した JavaScript ファイルを用意する必要があります。
これを CSS.paintWorklet に追加することで、CSS から Worklet を呼び出すことができるようになります。

Painting API の Worklet は registerPaint 関数を使うことで作成できます。

registerPaint('painting-name', class {
  paint(context) {
    // 描画処理
  }
})

第一引数 … Worklet 名( CSS の paint 関数から呼び出すときの名前)
第二引数 … paint という名前の関数が定義されたクラス

paint の引数に渡ってきた context( Canvas 2D Context 相当)に描画していくことになります。

CanvasRenderingContext2D
https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D

PaintRenderingContext2D
https://drafts.css-houdini.org/css-paint-api/#2d-rendering-context

描画コンテキストのサイズ取得

context のサイズを取得するには、paint 関数の第二引数を利用します。

👇context 全体を黒く塗りつぶす例です。

registerPaint('painting-name', class {
  paint(context, size) {
    const width = size.width
    const height = size.height
    context.fillRect(0, 0, width, height)
  }
})

CSS 変数の取得

paint 関数の第三引数を利用して、CSS 変数を参照することができます。

.paint-target {
  --fill-color: cyan;
  background-image: paint(fill)
}
registerPaint('fill', class {
  static get inputProperties() {
    return ['--fill-color']
  }

  paint(context, size, properties) {
    context.fillStyle = properties.get('--fill-color')
    context.fillRect(0, 0, size.width, size.height)
  }
})

これにより、カーソル位置などの情報を Worklet に渡すことが可能となります。

(これは不具合なのか分からないのですが、CSS 変数は body の子孫要素の Worklet でしか正しく取得できませんでした。なんだろうこれ。)