1. はじめに
CSSのペイント段階は、レイアウト段階で生成されたボックスのサイズおよび計算されたスタイルに基づいて、ボックスの背景、内容、ハイライトを描画する役割を担っています。
本仕様は、サイズや計算スタイルの変更に応じて、追加の<image> 関数を用いて、開発者がボックスの一部を描画できるAPIについて記述します。
注: 将来のバージョンの仕様では、クリップ領域やグローバルアルファ、背景レイヤーなどボックスの一部に対してフィルターを定義する機能が追加される可能性があります。
2. ペイントワークレット
現時点で一つのエンジンのみ対応。
Opera52以上Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile47以上
paintWorklet
属性は、ペイントに関連するすべてのクラスを管理する Worklet
へのアクセスを提供します。
paintWorklet
の
workletグローバルスコープ型は PaintWorkletGlobalScope
です。
paintWorklet
の
workletデスティネーション型は "paintworklet"
です。
partial namespace CSS { [SameObject ]readonly attribute Worklet ; };
paintWorklet
PaintWorkletGlobalScope
は、paintWorklet
のグローバル実行コンテキストです。
現時点で一つのエンジンのみ対応。
Opera?Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile?
PaintWorkletGlobalScope
には devicePixelRatio
プロパティがあり、Window.devicePixelRatio
プロパティと同じ値となります。
現時点で一つのエンジンのみ対応。
Opera?Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile?
[Global =(Worklet ,PaintWorklet ),Exposed =PaintWorklet ]interface :
PaintWorkletGlobalScope WorkletGlobalScope {undefined registerPaint (DOMString ,
name VoidFunction );
paintCtor readonly attribute unrestricted double ; };
devicePixelRatio
PaintRenderingContext2DSettings
はペイントキャンバスに関連付けられたレンダリングコンテキストの設定を保持します。PaintRenderingContext2DSettings
は、canvasの2Dレンダリングコンテキストのサポートされるサブセットを提供します。将来的にはペイントキャンバスの色管理のサポートなど拡張される可能性があります。
dictionary {
PaintRenderingContext2DSettings boolean =
alpha true ; };
class MyPaint{ static get inputProperties() { return [ '--foo' ]; } static get inputArguments() { return [ '<color>' ]; } static get contextOptions() { return { alpha: true }; } paint( ctx, size, styleMap) { // ここにペイントコードを記述 } }
3. 概念
ペイント定義は、
構造体であり、作者定義の
PaintWorkletGlobalScope
に必要な情報を記述します。これは <image>
(<paint()> 関数から参照可能)についての情報です。内容は次の通りです:
-
クラスコンストラクター( constructor )。
-
コンストラクター有効フラグ。
-
入力プロパティ( DOMStringのリスト)。
-
PaintRenderingContext2DSettingsオブジェクト。
文書ペイント定義は、 構造体であり、 作者定義の 文書が必要とする <image> 関数(ペイント関数から参照可能)について記述します。内容は次の通りです:
-
入力プロパティ( DOMStringのリスト)。
-
PaintRenderingContext2DSettingsオブジェクト。
4. カスタムペイントの登録
document は document paint
definitions の マップ を持ちます。初期状態ではこのマップは空です。registerPaint(name, paintCtor)
が呼び出されるとマップが追加されます。
PaintWorkletGlobalScope
は paint
definitions の マップ を持ちます。初期状態ではこのマップは空です。registerPaint(name, paintCtor)
が呼び出されるとマップが追加されます。
PaintWorkletGlobalScope
は paint
class instances の マップ を持ちます。初期状態ではこのマップは空です。draw a paint image
がユーザーエージェントにより実行されると追加されます。
paint class instances マップ内のペイントクラスのインスタンスは、ユーザーエージェントによっていつでも破棄・削除されることがあります。これは <paint()> 関数が使用されなくなった場合や、ユーザーエージェントがメモリを回収する必要がある場合などです。
現時点で一つのエンジンのみ対応。
Opera?Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile?
registerPaint(name, paintCtor)
メソッドが呼び出された場合、ユーザーエージェントは以下の手順を実行しなければならない:
-
paintDefinitionMap を
PaintWorkletGlobalScope
の paint definitions マップとする。 -
paintDefinitionMap[name] が 存在する場合は throw "
InvalidModificationError
"DOMException
を投げて処理を中止する。 -
inputProperties を空の
sequence<DOMString>
とする。 -
inputPropertiesIterable を Get(paintCtor, "inputProperties") の結果とする。
-
inputPropertiesIterable が undefined でなければ inputProperties に 変換した inputPropertiesIterable の
sequence<DOMString>
をセットする。例外が throw された場合は再throwして処理を中止する。 -
inputProperties をフィルタし、サポートされているCSSプロパティとカスタムプロパティのみを含める。
注: inputProperties getter で提供されるCSSプロパティのリストは、カスタムまたはネイティブCSSプロパティのどちらでもよいです。
注: CSSプロパティリストには省略プロパティ(ショートハンド)が含まれる場合があります。
注: ペイント画像クラスを将来互換とするため、現在ユーザーエージェントで無効なプロパティ(例:
margin-bikeshed-property
)も含まれる場合があります。
-
inputArguments を空の
sequence<DOMString>
とする。 -
inputArgumentsIterable を Get(paintCtor, "inputArguments") の結果とする。
-
inputArgumentsIterable が undefined でなければ inputArguments に 変換した inputArgumentsIterable の
sequence<DOMString>
をセットする。例外が発生した場合は再throwして処理を中止する。 -
inputArguments の各 item について、以下のサブ手順を実行する:
-
item から構文定義を消費しようとする。 失敗した場合は throw TypeError を投げて処理を中止する。 成功した場合は parsedSyntax を返された 構文定義とする。
-
parsedSyntax を inputArgumentSyntaxes に追加する。
-
-
contextOptionsValue を Get(paintCtor, "contextOptions") の結果とする。
-
paintRenderingContext2DSettings を 変換した contextOptionsValue の
PaintRenderingContext2DSettings
とする。 例外が throw された場合は再throwして処理を中止する。注:
paintRenderingContext2DSettings.alpha
をfalse
に設定すると、ユーザーエージェントはテキストのアンチエイリアスや「可視性」最適化(例:ペイント画像が不透明な場合は背面を描画しない)を行うことができます。 -
IsConstructor(paintCtor) の結果が false なら throw TypeError を投げて処理を中止する。
-
prototype を Get(paintCtor, "prototype") の結果とする。
-
Type(prototype) の結果が Object でない場合は throw TypeError を投げて処理を中止する。
-
paintValue を Get(prototype, "paint") の結果とする。
-
paint を 変換した paintValue の Function callback function 型とする。変換時に例外が発生した場合は再throwする。
-
definition を新しい ペイント定義として、以下とする:
-
クラスコンストラクターは paintCtor。
-
ペイント関数は paint。
-
コンストラクター有効フラグ はtrue。
-
input propertiesは inputProperties。
-
PaintRenderingContext2DSettingsオブジェクトは paintRenderingContext2DSettings。
-
-
paintDefinitionMap[name] に definition をセットする。
-
タスクをキューに追加し、以下の手順を実行する:
-
documentPaintDefinitionMap を関連する document の document paint definitions マップとする。
-
documentDefinition を新しい 文書ペイント定義として、以下とする:
-
input properties は inputProperties。
-
input argument syntaxes は inputArgumentSyntaxes。
-
PaintRenderingContext2DSettingsオブジェクト は paintRenderingContext2DSettings。
-
-
documentPaintDefinitionMap[name] が 存在する場合、以下の手順を実行:
-
existingDocumentDefinition を get documentPaintDefinitionMap[name] の結果とする。
-
existingDocumentDefinition が
"invalid"
なら、手順を中止する。 -
existingDocumentDefinition と documentDefinition が等しくない場合(すなわち input properties・input argument syntaxes・PaintRenderingContext2DSettingsオブジェクトが異なる場合)、次を実行する:
documentPaintDefinitionMap[name] に
"invalid"
をセットする。同じクラスが異なる
inputProperties
・inputArguments
・paintRenderingContext2DSettings
で登録されたことをデバッグコンソールにエラーとして記録する。
-
-
そうでなければ documentPaintDefinitionMap[name] に documentDefinition をセットする。
-
注: input properties のリストは一度だけ取得されるべきであり、クラスが動的にinput propertiesを変更することはできません。
注:
将来の仕様では、作者が異なる種類のRenderingContextを受け取れるようになる可能性があります。特に作者がWebGLレンダリングコンテキストを利用して3D効果を描画したい場合などです。WebGLコンテキストのセットアップには
PaintSize
や StylePropertyMap
を入力として渡すなどの複雑さがあります。
5. ペイント記法
現時点で一つのエンジンのみ対応。
Opera52以上Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.2以上Opera Mobile47以上
paint() = paint( <ident>, <declaration-value>? )
<paint()> 関数は <image> 型がサポートする追加の記法です。
< style > . logo { background-image : paint ( company -logo ); } . chat-bubble { background-image : paint ( chat -bubble , blue ); } </ style >
cursor プロパティにおいて、<paint()> 関数は 無効な画像として扱われ、次のサポートされている <image> にフォールバックします。
computed value の時点では <paint()> 関数は registerPaint()
で登録された文法と一致する必要はありません。
その代わり、無効な画像となり、
draw a paint image
内でパース時に発生します。
6. 2Dレンダリングコンテキスト
[Exposed =PaintWorklet ]interface { };
PaintRenderingContext2D PaintRenderingContext2D includes CanvasState ;PaintRenderingContext2D includes CanvasTransform ;PaintRenderingContext2D includes CanvasCompositing ;PaintRenderingContext2D includes CanvasImageSmoothing ;PaintRenderingContext2D includes CanvasFillStrokeStyles ;PaintRenderingContext2D includes CanvasShadowStyles ;PaintRenderingContext2D includes CanvasRect ;PaintRenderingContext2D includes CanvasDrawPath ;PaintRenderingContext2D includes CanvasDrawImage ;PaintRenderingContext2D includes CanvasPathDrawingStyles ;PaintRenderingContext2D includes CanvasPath ;
注: PaintRenderingContext2D
は CanvasRenderingContext2D
APIのサブセットを実装します。
具体的には CanvasImageData
、
CanvasUserInterface
、
CanvasText
、
CanvasTextDrawingStyles
APIは実装されません。
PaintRenderingContext2D
オブジェクトは 出力ビットマップ を持ちます。
これはオブジェクト作成時に初期化されます。
出力ビットマップのサイズは、描画先オブジェクトの具象オブジェクトサイズです。
PaintRenderingContext2D
オブジェクトは alpha フラグも持ちます。
このフラグは true または false に設定できます。
コンテキスト作成時は、alphaフラグはtrueに設定されます。
PaintRenderingContext2D
オブジェクトのalphaフラグがfalseの場合、
すべてのピクセルのアルファチャンネルは1.0(完全不透明)に固定され、ピクセルのアルファ成分を変更しようとしても黙って無視されます。
出力ビットマップのサイズは、ユーザーエージェントが内部的または描画時に使用する実際のビットマップサイズを必ずしも表しません。例えば、視覚ビューポートがズームされている場合、ユーザーエージェントは座標空間内のデバイスピクセル数に対応するビットマップを内部で使用し、高品質な描画結果を得ることができます。
さらにユーザーエージェントは、出力ビットマップに適用された描画操作のシーケンスを記録し、正しい解像度でデバイスビットマップに描画できるようにすることがあります。これにより、例えば視覚ビューポートがズームされている間も同じ出力ビットマップの出力を繰り返し再利用することが可能です。
"currentColor"
が PaintRenderingContext2D
APIで色として使われた場合は、不透明な黒として扱われます。
registerPaint( 'currentcolor' , class { paint( ctx, size) { ctx. fillStyle= 'currentColor' ; ctx. fillRect( 0 , 0 , size. width, size. height); } });
-
新しい
PaintRenderingContext2D
を作成する。 -
コンテキストのビットマップ寸法を設定し、出力ビットマップのサイズをwidthとheightの丸め値にする。
-
PaintRenderingContext2D
の alphaフラグを paintRenderingContext2DSettingsのalpha
で設定する。 -
新しい
PaintRenderingContext2D
を返す。
注: レンダリングコンテキストの初期状態はset bitmap dimensionsアルゴリズム内部で設定され、レンダリングコンテキストをデフォルト状態にリセットし、出力ビットマップもクリアされます。
6.1. CSSImageValueの描画
CanvasImageSource
型は、画像ソースとしてCSSImageValue
型も利用できるように拡張されます。
CanvasDrawImage
ミックスインを利用するインターフェースについて:
-
CanvasImageSource
オブジェクトがCSSImageValue
を表す場合、 その値の基礎画像アルゴリズムの結果がdrawImage
の画像ソースとして使用されなければなりません。
注: これは最終的にHTML仕様のcanvasセクションへ移動されるべきです。 Issue 819も参照してください。
7. 画像の描画
<paint()> 関数の画像が ボックス内で視覚ビューポート内にある場合、ユーザーエージェントは draw a paint image アルゴリズムの呼び出し結果の画像を表示しなければならない。
注: ユーザーエージェントは、視覚ビューポート内の <paint()> 関数について毎フレーム draw a paint image を実行する必要はありません。結果をキャッシュ(追加の無効化ステップで)して、正しい画像を表示できます。
注: ユーザーエージェントは視覚ビューポート外の画像描画を遅延させても構いません。
requestAnimationFrame
内でスタイルを更新する場合:
requestAnimationFrame( function () { element. styleMap. set( '--custom-prop-invalidates-paint' , 42 ); });
そして element
が視覚ビューポート内にある場合、ユーザーエージェントは現在のフレームで draw a paint image
を実行し、その結果を表示しなければなりません。
draw a paint image 関数は、object size negotiation アルゴリズムの間にユーザーエージェントによって呼び出されます。これは <image> を描画するためのアルゴリズムです。snappedConcreteObjectSize は以下の通り定義されます。concreteObjectSize は concrete object size です。通常 snappedConcreteObjectSize は concreteObjectSize と同じですが、ユーザーエージェントがピクセル境界に合わせてサイズ調整することがあり、その場合は元のサイズからの比率で snappedConcreteObjectSize を調整します。<paint()> 関数はこのサイズ調整に応じて描画できます。
object size negotiation アルゴリズムにおいて、ペイント画像は自然寸法を持ちません。
注: 将来の仕様では、作者がペイント画像の自然寸法を指定できるようになる可能性があります。これはコールバックとして静的な自然寸法や、計算スタイルやサイズの変化に応じて動的に自然寸法を定義できる形で公開される見込みです。
PaintSize
オブジェクトは、作者が描画すべき画像のサイズを表します。これはユーザーエージェントから渡される snappedConcreteObjectSize です。
注: CSS Images 3 § 4.4 CSSオブジェクトサイズ例を参照すると、concrete object size の計算方法が分かります。
draw a paint image 関数は、ユーザーエージェントによって、任意のタイミングで任意の snappedConcreteObjectSize で推測的に呼び出される場合があります。その結果画像は表示されません。
注: ユーザーエージェントは、snappedConcreteObjectSize の将来的な値(例えばサイズが変わらないと推測するなど)を予測するために、任意のヒューリスティックを用いることができます。
注: 画像は表示されませんが、キャッシュされる可能性があり、以降の <paint()> の呼び出しでキャッシュ画像が使われる場合があります。
[Exposed =PaintWorklet ]interface {
PaintSize readonly attribute double ;
width readonly attribute double ; };
height
-
paintFunction を描画対象の box 上の <paint()> 関数とする。
-
name を paintFunction の第1引数とする。
-
documentPaintDefinitionMap を関連する document の document paint definitions マップとする。
-
documentPaintDefinitionMap[name] が 存在しない場合、画像出力は 無効な画像とし、以降の手順を中止する。
-
documentDefinition を get documentPaintDefinitionMap[name] の結果とする。
-
documentDefinition が
"invalid"
の場合、画像出力は 無効な画像とし、以降の手順を中止する。 -
inputArgumentSyntaxes を documentDefinition の input argument syntaxes とする。
-
inputArguments を paintFunction の "paint name" 引数以降の全引数の リストとする。
-
inputArguments が inputArgumentSyntaxes の登録文法と一致しない場合、画像出力は 無効な画像とし、以降の手順を中止する。
このステップは以下のような場合に失敗します:// paint.js registerPaint( 'failing-argument-syntax' , class { static get inputArguments() { return [ '<length>' ]; } paint( ctx, size, styleMap, args) { /* paint code here. */ } }); < style > . example-1 { background-image : paint ( failing -argument-syntax , red ); } . example-2 { background-image : paint ( failing -argument-syntax , 1 px , 2 px ); } </ style > < div class = example-1 ></ div > < div class = example-2 ></ div > < script > CSS. paintWorklet. addModule( 'paint.js' ); </ script > example-1
は 無効な画像となります。理由は"red"
が登録文法に一致しないためです。example-2
は 無効な画像となります。理由は引数が多すぎるためです。 -
workletGlobalScope を
PaintWorkletGlobalScope
のいずれかとして、§ 7.1 グローバルスコープ選択の規則に従う。ユーザーエージェントはこの時点で
Worklet
から worklet global scope を作成してもよい。 -
invoke a paint callback を name、inputArguments、snappedConcreteObjectSize、workletGlobalScope で実行、必要なら 並列で。
注: ユーザーエージェントが invoke a paint callback をスレッドで並列実行する場合、そのスレッドで使えるペイントワークレットグローバルスコープを選択するべきです。
-
paintDefinitionMap を workletGlobalScope の paint definitions マップとする。
-
paintDefinitionMap[name] が 存在しない場合は、次の手順を実行:
-
タスクをキューに追加し、次の手順を実行:
-
documentPaintDefinitionMap を関連する document の document paint definitions マップとする。
-
documentPaintDefinitionMap[name] に
"invalid"
をセットする。 -
ユーザーエージェントは、全
PaintWorkletGlobalScope
でクラスが登録されていない旨をデバッグコンソールにエラーとして記録するべき。
-
-
画像出力は 無効な画像とし、以降の手順を中止する。
注: これは、あるペイントワークレットグローバルスコープが
registerPaint(name, paintCtor)
を受け取らなかった場合を扱います(他のグローバルスコープが受け取った場合も)。他のグローバルスコープでペイントコールバックが成功しても、以降のフレームで draw a paint image が呼ばれると成功しません。 -
-
definition を get paintDefinitionMap[name] の結果とする。
-
paintClassInstanceMap を workletGlobalScope の paint class instances マップとする。
-
paintInstance を get paintClassInstanceMap[|name|] の結果とする。paintInstance が null の場合は次の手順を実行:
-
definition の constructor valid flag が false の場合、画像出力は 無効な画像とし、以降の手順を中止する。
-
paintCtor を definition の class constructor とする。
-
paintInstance を Construct(paintCtor) の結果とする。
もし construct が例外を投げた場合は、definition の constructor valid flag を false にし、画像出力は 無効な画像とし、以降の手順を中止する。
-
paintClassInstanceMap[name] に paintInstance をセットする。
-
-
inputProperties を definition の input properties とする。
-
styleMap を
StylePropertyMapReadOnly
の新規インスタンスとして、inputProperties に列挙されるプロパティの computed value のみを設定する。 -
renderingContext を create a PaintRenderingContext2D object で次を渡して生成する:
-
"width" - snappedConcreteObjectSize の幅。
-
"height" - snappedConcreteObjectSize の高さ。
-
"paintRenderingContext2DSettings" - definition の PaintRenderingContext2DSettings オブジェクト。
注: renderingContext はペイントの呼び出し間で再利用されません。つまり、呼び出し間で renderingContext に格納されたデータや状態はありません。例えば、コンテキストにクリップを設定しても、次回のペイントメソッド実行時に同じクリップは適用されません。
注: また、ペイントメソッド終了後は renderingContext は実質的に「無効化」されます。作者コードが renderingContext を参照してメソッドを呼び出しても、現在の画像や以降の画像には影響しません。
-
-
paintSize を
PaintSize
の新規インスタンスとして snappedConcreteObjectSize の幅・高さで初期化する。 -
この段階で、ユーザーエージェントは paintSize、styleMap、inputArguments が前回と同じなら以前の呼び出しの画像を再利用してもよい。その場合は画像出力をキャッシュ画像とし、以降の手順を中止する。
下記の例では、div-1
とdiv-2
はペイント関数のJavaScript引数が同じです。ユーザーエージェントは一方の呼び出し結果をキャッシュし、両方の要素で利用できます。// paint.js registerPaint( 'simple' , class { paint( ctx, size) { ctx. fillStyle= 'green' ; ctx. fillRect( 0 , 0 , size. width, size. height); } }); < style > . div-1 { width : 50 px ; height : 50 px ; background-image : paint ( simple ); } . div-2 { width : 100 px ; height : 100 px ; background-size : 50 % 50 % ; background-image : paint ( simple ); } </ style > < div class = div-1 ></ div > < div class = div-2 ></ div > < script > CSS. paintWorklet. addModule( 'paint.js' ); </ script > -
paintFunctionCallback を definition の paint function とする。
-
Invoke paintFunctionCallback を引数 «renderingContext, paintSize, styleMap, inputArguments» で、paintInstance を callback this value として呼び出す。
もし paintFunctionCallback が適切な時間内に完了しない場合(ユーザーエージェントが「長時間実行スクリプト」と判断)、ユーザーエージェントはスクリプトを強制終了してもよい。画像出力は 無効な画像とし、以降の手順を中止する。
注: ユーザーエージェントは、デバッグツールで作者にペイントクラスのコストを可視化したり、「応答しないスクリプト」ダイアログを表示するなどのツールを提供できます。
-
画像出力はメソッドに渡された renderingContext から生成する。
注: 生成画像の内容はアクセシビリティ目的には設計されていません。作者は必要な情報を標準アクセシビリティAPIで提供してください。
7.1. グローバルスコープの選択
ユーザーエージェントが paint PaintWorkletGlobalScope
を paint Worklet
の
global scopes リストから選択する必要がある場合、次を満たす必要がある:
-
ユーザーエージェントがメモリ制約下でない限り、最低でも2つ以上の
PaintWorkletGlobalScope
から選択する。 -
同じ
PaintWorkletGlobalScope
を1000回以上連続して再利用しない。注: 1000回という上限は高めに設定されていますが、今後この制限は(下方向に)改善される可能性があります。
注: これらの規則は、作者がグローバルオブジェクトやクラス上に状態を保存できることに依存しないようにするために存在します。詳細は worklets仕様のコードの冪等性についての議論を参照してください。
8. 例
8.1. 例1: 色付き円
下記の例では <paint()> 関数がアニメーション可能であることを利用しています。
例えば、下記の例で textarea にフォーカスすると --circle-color
プロパティが deepskyblue
から
purple
へと遷移します。
この機能はトランジションだけでなく、CSSアニメーションやWeb Animations APIにも適用できます。
<!DOCTYPE html> < style > # example { --circle-color : deepskyblue ; background-image : paint ( circle ); font-family : sans-serif ; font-size : 36 px ; transition : -- circle - color 1 s ; } # example : focus { --circle-color : purple ; } </ style > < textarea id = "example" > CSS is awesome.</ textarea > < script > CSS. registerProperty({ name: '--circle-color' , syntax: '<color>' , initialValue: 'black' , inherits: false }); CSS. paintWorklet. addModule( 'circle.js' ); </ script >
// circle.js registerPaint( 'circle' , class { static get inputProperties() { return [ '--circle-color' ]; } paint( ctx, geom, properties) { // 塗りつぶし色を変更 const color= properties. get( '--circle-color' ); ctx. fillStyle= color. cssText; // 中心点と半径を決定 const x= geom. width/ 2 ; const y= geom. height/ 2 ; const radius= Math. min( x, y); // 円を描画 ctx. beginPath(); ctx. arc( x, y, radius, 0 , 2 * Math. PI, false ); ctx. fill(); } });
8.2. 例2: 画像プレースホルダー
画像が読み込み中の間、paintを使ってプレースホルダー画像を描画することができます。
<!DOCTYPE html> < style > # example { --image : url( '#someUrlWhichIsLoading' ); background-image : paint ( image -with-placeholder ); } </ style > < div id = "example" ></ div > < script > CSS. registerProperty({ name: '--image' , syntax: '<image> | none' , initialValue: 'none' , }); CSS. paintWorklet. addModule( 'image-placeholder.js' ); </ script >
// image-placeholder.js registerPaint( 'image-with-placeholder' , class { static get inputProperties() { return [ '--image' ]; } paint( ctx, geom, properties) { const img= properties. get( '--image' ); switch ( img. state) { case 'ready' : // 画像が読み込まれた!画像を描画 ctx. drawImage( img, 0 , 0 , geom. width, geom. height); break ; case 'pending' : // 画像が読み込み中、山のイラストを描画 drawMountains( ctx); break ; case 'invalid' : default : // 画像が無効(例: 読み込み失敗)、悲しい顔を描画 drawSadFace( ctx); break ; } } });
8.3. 例3: 弧(アーク)
<!DOCTYPE html> < style > # example { width : 200 px ; height : 200 px ; background-image : paint ( arc , purple , 0.4 turn , 0.8 turn , 40 px , 15 px ), paint ( arc , blue , -20deg , 170 deg , 30 px , 20 px ), paint ( arc , red , 45 deg , 220 deg , 50 px , 10 px ); } </ style > < div id = "example" ></ div > < script > CSS. paintWorklet. addModule( 'arc.js' ); </ script >
// arc.js registerPaint( 'arc' , class { static get inputArguments() { return [ '<color>' , '<angle>' , // 開始角度 '<angle>' , // 終了角度 '<length>' , // 半径 '<length>' , // 線幅 ]; } paint( ctx, geom, _, args) { ctx. strokeStyle= args[ 0 ]. cssText; // 中心点を決定 const x= geom. width/ 2 ; const y= geom. height/ 2 ; // 開始角度・終了角度をラジアンに変換 const startAngle= this . convertAngle( args[ 1 ]) - Math. PI/ 2 ; const endAngle= this . convertAngle( args[ 2 ]) - Math. PI/ 2 ; // 半径・線幅をpxに変換 const radius= this . convertLength( args[ 3 ]); const lineWidth= this . convertLength( args[ 4 ]); ctx. lineWidth= lineWidth; ctx. beginPath(); ctx. arc( x, y, radius, startAngle, endAngle, false ); ctx. stroke(); } convertAngle( angle) { switch ( angle. unit) { case 'deg' : return angle. value* Math. PI/ 180 ; case 'rad' : return angle. value; case 'grad' : return angle. value* Math. PI/ 200 ; case 'turn' : return angle. value* Math. PI/ 0.5 ; default : throw Error( `未知の角度単位: ${ angle. unit} ` ); } } convertLength( length) { switch ( length. type) { case 'px' : return length. value; default : throw Error( `未知の長さタイプ: ${ length. type} ` ); } } });
8.4. 例4: サイズに応じた色の違い
< h1 > Heading 1</ h1 > < h1 > Another heading</ h1 > < style > h1 { background-image : paint ( heading -color ); } </ style > < script > CSS. paintWorklet. addModule( 'heading-color.js' ); </ script >
// heading-color.js registerPaint( 'heading-color' , class { static get inputProperties() { return []; } paint( ctx, geom, properties) { // 画像の幅・高さに応じて色を選択 const width= geom. width; const height= geom. height; const color= colorArray[( width* height) % colorArray. length]; // 単色で塗りつぶし ctx. fillStyle= color; ctx. fillRect( 0 , 0 , width, height); } });
8.5. 例5: 要素領域外への描画
border-image プロパティを使うことで、要素の領域外にも描画することが可能です。
< style > # overdraw { --border-width : 10 ; border-style : solid ; border-width : calc( var ( --border-width ) * 1 px ); border-image-source : paint ( overdraw ); border-image-slice : 0 fill ; border-image-outset : calc( var ( --border-width ) * 1 px ); width : 200 px ; height : 200 px ; } </ style > < div id = "overdraw" ></ div > < script > CSS. paintWorklet. addModule( 'overdraw.js' ); </ script >
// overdraw.js registerPaint( 'overdraw' , class { static get inputProperties() { return [ '--border-width' ]; } paint( ctx, geom, properties) { const borderWidth= parseInt( properties. get( '--border-width' )); ctx. shadowColor= 'rgba(0,0,0,0.25)' ; ctx. shadowBlur= borderWidth; ctx. fillStyle= 'rgba(255, 255, 255, 1)' ; ctx. fillRect( borderWidth, borderWidth, geom. width- 2 * borderWidth, geom. height- 2 * borderWidth); } });
9. セキュリティに関する考慮事項
これらの機能によって新たなセキュリティ問題は知られていません。
10. プライバシーに関する考慮事項
-
ペイントコールバックのタイミングは、リンクが「訪問済み」状態かどうかを検出するための高帯域チャネルとして利用できます。 (詳細) これは本質的に新しいプライバシー漏洩ではなく、 訪問済み状態は多くのインタラクションから漏洩しますが、 他の緩和策がない場合、 これは特に情報の高帯域チャネルとなります。
現時点では公式な緩和策は計画されていません。 このプライバシー漏洩は、すべてのそのようなチャネルを修正するために より直接的に対処する必要があります。
11. 変更点
2018年8月9日 CR公開以降の変更点:
-
ペイントワークレットへの入力プロパティのリストを既知またはカスタムプロパティのみになるように絞り込みました。
-
PaintRenderingContext2D
にアルファフラグを追加し、レンダリングサーフェスが強制的に不透明になるか、透過を許可するかを制御できるようにしました。 -
出力ビットマップのサイズ定義を修正:
出力ビットマップのサイズは レンダリング先オブジェクトの具体的なオブジェクトサイズ
レンダリングしているフラグメントのサイズです。