CSS paint apiを使って複雑な背景描画を実現する

CSS paint apiを使って複雑な背景描画を実現する

CSS paint apiを使って複雑な背景描画を実現する

CSS Paint APIとは、CSSの背景や枠線の描画をjavascriptでプログラム描画する機能です。

描画した図形画像をアニメーションさせたり、受け取るパラメータによって変更したりする事もできます。

JSで描画するのでプログラム知識が必要になりますが、高度な演出が可能になります。

CSS Paint APIとは

本記事ではCSSのスタイリングにjavascriptを使う方法をご紹介します。

それがCSS Houdini(フーディーニ)APIという機能です。

CSS HoudiniのCSS Paint API

CSS HoudiniのAPIは、CSSの機能をJavaScirptによる描画処理を利用して拡張した機能です。

より高度で複雑なCSSスタイルをプログラミング感覚で実現する事ができます。

今回はCSS HoudiniのAPIの一部となる、現在Google ChromeやEdgeで実装されている「CSS Paint API」を紹介します。

CSS Paint APIを使用すれば、JavaScript関数を記述する事でCSS要素の背景や境界線、またはコンテンツに直接描画ができるようになります。

CSSに高度で複雑な背景画像・処理が利用できる

分かりやすく言うと、CSSで用いる画像をJavaScriptから動的に生成するためのAPIと言えます。

生成した画像は、background-imageやborder-imageで利用可能です。

様々な演出効果が実現できる

今までcanvas要素で無理やり実現していた複雑な背景なども、CSSの中で実現できるようになる訳です。

画像は単なる静止画だけでなく、アニメーションやパラメータを使ったバリエーションも作成できます。

このAPIはローカル上では動作確認できないため、サーバー上で確認しましょう。

実装と基本的な仕組みの解説

それでは各工程を解説しながら、具体的に実装をしていきます。

HTMLソース

<div class="box"></div>

まず適当にdivにクラス名を付けて、上記の様なHTMLソースを書きましょう。

workletのJSコードを続けて追記

<script> if (CSS.paintWorklet) { CSS.paintWorklet.addModule('js/paint.js'); } </script>

上記ソースに続けて、<script>でcssにpaintworkletを追加し、モジュール読込先のjsファイルを指定します。

Workletとは

CSS Paint APIを使用するには、まずWorkerの一種であるWorkletの作成が必要です。

記述されているPaintWorkletは、Workletの中でもCSS Paint API向けの描画に関係する機能を備えています。

PaintWorkletの描画ファイルを読込

PaintWorkletはメインスレッド(UIスレッド)で動作します。

PaintWorkletのモジュール読込先となるJavaScriptファイル(js/paint.js)を用意します。

その中に任意の処理を書く事で、.boxのCSS描画を拡張していく訳です。

CSS

.box { height: 100px; background: paint(draw); }

設定したCSSクラス名に対し、backgroundでpaint()メソッドを設定します。

ここでは「draw」の名称を付けています。

JS

registerPaint('draw', class { paint(ctx, size) { ctx.fillStyle = 'red'; ctx.fillRect(0, 0, size.width, size.height); } });

JSファイルの中で、まずregisterPaint関数でdrawクラスを登録します。

同時にcssのpaintメソッド(draw)における描画処理を作成します。

コード中のpaint()のところが描画をしている部分で、今回は1色べた塗しています。

この登録したdrawクラス名をCSS側から利用する事になりますね。

事例サンプル:赤いパネル描画

この赤い帯はCSSのbackground-colorで演出したものではなく、Javascriptによる描画になります。

paintメソッド変数の追加

paintメソッドは、propertiesのところでCSSのプロパティに使うパラメータを受け取る事ができます。

CSSプロパティをパラメータとして使うことで、場面ごとの柔軟なバリエーションを作る事ができます。

クラスにstatic get inputPropertiesを実装し、読み取りたいプロパティの名前の配列を返します。

そしてpaintメソッドの第3引数であるpropertiesからgetメソッドを使って値を取得します。

HTML

<div class="box" style="--color:red"></div> <div class="box" style="--color:blue"></div>

上記はCSSの方ではなく、直接スタイルを書き込んだ状態です。

CSS

.box { height: 100px; /* --color: blue; */ background:paint(draw); }

上記はコメントアウトしていますが、本来はこちらに書いても良いと思います。

JS

registerPaint('draw', class { static get inputProperties() {return ['--color']} paint(ctx, size, properties) { const c = properties.get('--color'); ctx.fillStyle = c; ctx.fillRect(0, 0, size.width, size.height); } });

この様にする事で、HTMLタグ或いはCSS上の引数に応じて、その色で敷き詰めた画像を描画する事ができます。

事例サンプル:パラメータに応じた色の変化

HTMLソース上でredとしたものは赤に、blueとしたものは青になっています。

アニメーション例

ポイントになるのはJSコード中でどのような演出効果を作るかになってきます。

本記事ではJS内の複雑な解説はしませんが、以下の様な複雑なアニメーション効果も実現できます。

サンプルを紹介しておきます。

マウスオーバーアニメーション例1:(六角形)

JSファイル

六角形によるマスク効果が入るアニメーション演出です。

registerPaint('fragmentation', class { static get inputProperties() { return [ '--f-r', '--f-o' ] } paint(ctx, size, properties) { const r = parseInt(properties.get('--f-r')); const o = properties.get('--f-o'); const w = size.width; const h = size.height; const a = 2 * Math.PI / 6; const l = 7; /* seeded random */ const mask = 0xffffffff; const seed = 30; let m_w = (123456789 + seed) & mask; let m_z = (987654321 - seed) & mask; let random = function() { m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask; var result = ((m_z << 16) + (m_w & 65535)) >>> 0; result /= 4294967296; return result; } /**/ for (let y = 0; y + r * Math.sin(a) < (h + 3*r); y += r * Math.sin(a)) { for (let x = 0, j = 0; x + r * (1 + Math.cos(a)) < (w + 3*r); x += r * (1 + Math.cos(a)), y += (-1) ** j++ * r * Math.sin(a)) { ctx.beginPath(); for (let i = 0; i < 6; i++) { ctx.lineTo(x + r * Math.cos(a * i), y + r * Math.sin(a * i)); } ctx.closePath(); var alpha = (random()*(l-1) + 1) - (1-o)*l; ctx.fillStyle = 'rgba(0,0,0,'+alpha+')'; ctx.strokeStyle = 'rgba(0,0,0,'+alpha+')'; ctx.stroke(); ctx.fill(); } } } })

マウスオーバーアニメーション例2:(四角形)

JSファイル

上と同じで今度は四角形によるマスク効果が入るアニメーション演出です。

registerPaint('fragmentation', class { static get inputProperties() { return [ '--f-n', '--f-m', '--f-o' ] } paint(ctx, size, properties) { const n = properties.get('--f-n'); const m = properties.get('--f-m'); const o = properties.get('--f-o'); const w = size.width/n; const h = size.height/m; const l = 10; const mask = 0xffffffff; const seed = 30; let m_w = (123456789 + seed) & mask; let m_z = (987654321 - seed) & mask; let random = function() { m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask; var result = ((m_z << 16) + (m_w & 65535)) >>> 0; result /= 4294967296; return result; } for(var i=0;i<n;i++) { for(var j=0;j<m;j++) { ctx.fillStyle = 'rgba(0,0,0,'+((random()*(l-1) + 1) - (1-o)*l)+')'; ctx.fillRect(i*w, j*h, w, h); } } } })

この記事をシェアする

一押し人気コーナー紹介

CSS関連記事