Chrome 65 の新機能
Chrome 65 以降では、CSS Paint API(「CSS Custom Paint」または「Houdini のペイント ワークレット」とも呼ばれます)がデフォルトで有効になっています。概要: 何ができますか?仕組みでは、続きをどうぞ。
CSS Paint API を使用すると、CSS プロパティが画像を必要とするたびに、プログラムで画像を生成できます。background-image
や border-image
などのプロパティは、通常、画像ファイルを読み込むための url()
や、linear-gradient()
などの CSS 組み込み関数で使用されます。これらを使用する代わりに、paint(myPainter)
を使用してペイント ワークレットを参照できます。
ペイント ワークレットを作成する
myPainter
というペイント ワークレットを定義するには、CSS.paintWorklet.addModule('my-paint-worklet.js')
を使用して CSS ペイント ワークレット ファイルを読み込む必要があります。このファイルで、registerPaint
関数を使用してペイント ワークレット クラスを登録できます。
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
paint()
コールバック内で、<canvas>
で使用した CanvasRenderingContext2D
と同じように ctx
を使用できます。<canvas>
で描画する方法がわかれば、ペイント ワークレットで描画できます。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>
を使用したことがある場合は、このコードに馴染みがあるはずです。こちらのライブデモをご覧ください。
一般的な背景画像を使用する場合との違いは、ユーザーが textarea のサイズを変更するたびに、パターンがオンデマンドで再描画されることです。つまり、高密度ディスプレイの補正を含め、背景画像は常に必要な大きさに正確に調整されます。
素晴らしいですが、静的でもあります。同じパターンでサイズの異なる正方形が必要な場合は、毎回新しいワークレットを作成する必要がありますか?答えは「いいえ」です。
ワークレットのパラメータ化
幸い、ペイント ワークレットは他の 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 のみです。他のすべてのブラウザ ベンダーから前向きなシグナルはありますが、進展はほとんどありません。最新情報を入手するには、Houdini は準備ができていますか?を定期的に確認してください。それまでは、ペイント ワークレットがサポートされていない場合でもコードを実行し続けられるように、段階的な拡張機能を使用してください。想定どおりに動作するようにするには、CSS と JS の 2 か所でコードを調整する必要があります。
JS でペイント ワークレットのサポートを検出するには、CSS
オブジェクトを確認します。
js
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}
CSS 側には、次の 2 つのオプションがあります。@supports
は次のように使用できます。
@supports (background: paint(id)) {
/* ... */
}
より簡潔な方法は、不明な関数がある場合、CSS がプロパティ宣言を無効にし、その後でその宣言全体を無視するという事実を利用することです。プロパティを 2 回指定する場合(最初はペイント ワークレットなしで、次にペイント ワークレットありで指定する場合)は、段階的な拡張が適用されます。
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
ペイント ワークレットをサポートしているブラウザでは、background-image
の 2 つ目の宣言が 1 つ目の宣言を上書きします。ペイント ワークレットをサポートしていないブラウザでは、2 番目の宣言は無効になり破棄され、1 番目の宣言が有効になります。
CSS ペイント ポリフィル
多くの用途では、CSS Paint Polyfill を使用することもできます。これにより、最新のブラウザに CSS カスタム ペイントとペイント ワークレットのサポートが追加されます。
ユースケース
ペイント ワークレットには多くのユースケースがあり、そのうちのいくつかは他よりも明白です。最もわかりやすい方法の 1 つは、ペイント ワークレットを使用して DOM のサイズを小さくすることです。多くの場合、要素は CSS を使用して装飾を作成するために追加されます。たとえば、マテリアル デザイン ライトでは、波紋効果のあるボタンに、波紋自体を実装するための <span>
要素が 2 つ追加されています。ボタンの数が多い場合、DOM 要素が多くなり、モバイルでのパフォーマンスが低下する可能性があります。代わりにペイント ワークレットを使用してリップル効果を実装すると、追加の要素は 0 個で、ペイント ワークレットは 1 つだけになります。また、カスタマイズとパラメータ化がはるかに簡単になります。
ペイント ワークレットを使用するもう 1 つの利点は、ほとんどの場合、ペイント ワークレットを使用したソリューションはバイト数が小さいことです。もちろんトレードオフはあります。キャンバスのサイズやパラメータが変更されるたびにペイント コードが実行されるということです。そのため、コードが複雑で時間がかかる場合、ジャンクが発生する可能性があります。Chrome は、長時間実行されるペイント ワークレットでもメインスレッドの応答性に影響を与えないように、ペイント ワークレットをメインスレッドから移動する作業を進めています。
私にとって最もエキサイティングな展望は、ペイント ワークレットによって、ブラウザにまだない CSS 機能を効率的にポリフィルできることです。たとえば、円錐形グラデーションが Chrome にネイティブに実装されるまで、ポリフィルで代用するなどです。もう一つの例は、CSS の会議で、枠線の色を複数設定できるようになったことです。会議がまだ始まっている間に、同僚の Ian Kilpatrick がペイント ワークレットを使用してこの新しい CSS 動作のポリフィルを作成しました。
既成概念にとらわれない考え方
背景画像や枠線の画像は、ペイント ワークレットについて学ぶとよくわかります。ペイント ワークレットの直感的でないユースケースの 1 つは、DOM 要素に任意の形状を持たせる mask-image
です。たとえば、ダイヤモンド:
mask-image
は、要素のサイズの画像を受け取ります。マスク画像が透明な領域、要素は透明です。マスク画像が不透明な領域、要素が不透明。
Chrome で利用可能に
ペイント ワークレットは、しばらくの間 Chrome Canary に存在していました。Chrome 65 では、デフォルトで有効になっています。ペイント ワークレットが提供する新しい可能性をお試しいただき、作成した作品をお見せください。さらにヒントが必要な場合は、Vincent De Oliveira のコレクションをご覧ください。