CSS ペインティング API レベル 1

W3C 候補勧告草案,

この文書の詳細情報
このバージョン:
https://www.w3.org/TR/2021/CRD-css-paint-api-1-20211216/
最新公開バージョン:
https://www.w3.org/TR/css-paint-api-1/
編集者草案:
https://drafts.css-houdini.org/css-paint-api-1/
以前のバージョン:
履歴:
https://www.w3.org/standards/history/css-paint-api-1
フィードバック:
public-houdini@w3.org 件名 “[css-paint-api] … メッセージのトピック …” (アーカイブ)
GitHub
実装レポート:
https://wpt.fyi/results/css/css-paint-api
編集者:
前編集者:

概要

ウェブ開発者が javascript を使ってカスタム CSS <image> を定義できる API であり、 スタイルやサイズの変更にも対応します。 詳細は EXPLAINER(解説) を参照してください。

この文書のステータス

このセクションは、公開時点での文書のステータスについて説明します。 現在の W3C 公開リストや この技術報告書の最新リビジョンは W3C 技術報告書インデックス(https://www.w3.org/TR/) で確認できます。

この文書は CSS ワーキンググループ により 候補勧告草案(Candidate Recommendation Draft) として 勧告トラックを用いて公開されました。 候補勧告として公開されたことは W3C およびその会員による承認を意味するものではありません。 候補勧告草案は、 ワーキンググループが次の候補勧告スナップショットに含める予定の 以前の候補勧告からの変更を統合したものです。

この文書はドラフトであり、 今後更新、置き換え、 または他文書によって廃止される可能性があります。 この文書を進行中の作業以外のものとして引用することは適切ではありません。

ご意見は GitHub で Issue を提出(推奨) してください。その際、タイトルに仕様コード “css-paint-api” を含めてください(例: “[css-paint-api] …コメント要約…”)。 すべての Issue やコメントは アーカイブされています。 または、(アーカイブ)パブリックメーリングリスト www-style@w3.org へ送信することもできます。

この文書は 2021年11月2日版 W3C プロセス文書 によって管理されています。

この文書は W3C 特許ポリシーの下で運営されるグループによって作成されました。 W3C は、グループの成果物に関連する公開特許開示リストを維持しています。 このページには特許開示方法も記載されています。 個人が特許について実際に知っており、その特許に必須クレームが含まれると考える場合は、 W3C 特許ポリシー第6節に従って情報を開示する必要があります。

1. はじめに

CSSのペイント段階は、レイアウト段階で生成されたボックスのサイズおよび計算されたスタイルに基づいて、ボックスの背景、内容、ハイライトを描画する役割を担っています。

本仕様は、サイズや計算スタイルの変更に応じて、追加の<image> 関数を用いて、開発者がボックスの一部を描画できるAPIについて記述します。

注: 将来のバージョンの仕様では、クリップ領域やグローバルアルファ、背景レイヤーなどボックスの一部に対してフィルターを定義する機能が追加される可能性があります。

2. ペイントワークレット

CSS/paintWorklet

現時点で一つのエンジンのみ対応。

Firefox未対応Safari未対応Chrome65以上
Opera52以上Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile47以上

paintWorklet 属性は、ペイントに関連するすべてのクラスを管理する Worklet へのアクセスを提供します。

paintWorkletworkletグローバルスコープ型PaintWorkletGlobalScope です。

paintWorkletworkletデスティネーション型"paintworklet" です。

partial namespace CSS {
    [SameObject] readonly attribute Worklet paintWorklet;
};

PaintWorkletGlobalScope は、paintWorklet のグローバル実行コンテキストです。

PaintWorklet/devicePixelRatio

現時点で一つのエンジンのみ対応。

Firefox未対応Safari未対応Chrome65以上
Opera?Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile?

PaintWorkletGlobalScope には devicePixelRatio プロパティがあり、Window.devicePixelRatio プロパティと同じ値となります。

PaintWorklet

現時点で一つのエンジンのみ対応。

Firefox未対応Safari未対応Chrome65以上
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()> 関数から参照可能)についての情報です。内容は次の通りです:

文書ペイント定義は、 構造体であり、 作者定義の 文書が必要とする <image> 関数(ペイント関数から参照可能)について記述します。内容は次の通りです:

4. カスタムペイントの登録

documentdocument paint definitionsマップ を持ちます。初期状態ではこのマップは空です。registerPaint(name, paintCtor) が呼び出されるとマップが追加されます。

PaintWorkletGlobalScopepaint definitionsマップ を持ちます。初期状態ではこのマップは空です。registerPaint(name, paintCtor) が呼び出されるとマップが追加されます。

PaintWorkletGlobalScopepaint class instancesマップ を持ちます。初期状態ではこのマップは空です。draw a paint image がユーザーエージェントにより実行されると追加されます。

paint class instances マップ内のペイントクラスのインスタンスは、ユーザーエージェントによっていつでも破棄・削除されることがあります。これは <paint()> 関数が使用されなくなった場合や、ユーザーエージェントがメモリを回収する必要がある場合などです。

PaintWorklet/registerPaint

現時点で一つのエンジンのみ対応。

Firefox未対応Safari未対応Chrome65以上
Opera?Edge79以上
Edge (レガシー)未対応IE未対応
Firefox for Android未対応iOS Safari未対応Chrome for Android65以上Android WebView65以上Samsung Internet9.0以上Opera Mobile?
registerPaint(name, paintCtor) メソッドが呼び出された場合、ユーザーエージェントは以下の手順を実行しなければならない:
  1. name が空文字列の場合、throw TypeError を投げて処理を中止する。

  2. paintDefinitionMapPaintWorkletGlobalScopepaint definitions マップとする。

  3. paintDefinitionMap[name] が 存在する場合は throw "InvalidModificationError" DOMException を投げて処理を中止する。

  4. inputProperties を空の sequence<DOMString>とする。

  5. inputPropertiesIterableGet(paintCtor, "inputProperties") の結果とする。

  6. inputPropertiesIterable が undefined でなければ inputProperties変換した inputPropertiesIterablesequence<DOMString> をセットする。例外が throw された場合は再throwして処理を中止する。

  7. inputProperties をフィルタし、サポートされているCSSプロパティカスタムプロパティのみを含める。

注: inputProperties getter で提供されるCSSプロパティのリストは、カスタムまたはネイティブCSSプロパティのどちらでもよいです。

注: CSSプロパティリストには省略プロパティ(ショートハンド)が含まれる場合があります。

注: ペイント画像クラスを将来互換とするため、現在ユーザーエージェントで無効なプロパティ(例: margin-bikeshed-property)も含まれる場合があります。

  1. inputArguments を空の sequence<DOMString>とする。

  2. inputArgumentsIterableGet(paintCtor, "inputArguments") の結果とする。

  3. inputArgumentsIterable が undefined でなければ inputArguments変換した inputArgumentsIterablesequence<DOMString> をセットする。例外が発生した場合は再throwして処理を中止する。

  4. inputArgumentSyntaxesリストとする。

  5. inputArguments の各 item について、以下のサブ手順を実行する:

    1. item から構文定義を消費しようとする。 失敗した場合は throw TypeError を投げて処理を中止する。 成功した場合は parsedSyntax を返された 構文定義とする。

    2. parsedSyntaxinputArgumentSyntaxes に追加する。

  6. contextOptionsValueGet(paintCtor, "contextOptions") の結果とする。

  7. paintRenderingContext2DSettings変換した contextOptionsValuePaintRenderingContext2DSettings とする。 例外が throw された場合は再throwして処理を中止する。

    注: paintRenderingContext2DSettings.alphafalse に設定すると、ユーザーエージェントはテキストのアンチエイリアスや「可視性」最適化(例:ペイント画像が不透明な場合は背面を描画しない)を行うことができます。

  8. IsConstructor(paintCtor) の結果が false なら throw TypeError を投げて処理を中止する。

  9. prototypeGet(paintCtor, "prototype") の結果とする。

  10. Type(prototype) の結果が Object でない場合は throw TypeError を投げて処理を中止する。

  11. paintValueGet(prototype, "paint") の結果とする。

  12. paint変換した paintValueFunction callback function 型とする。変換時に例外が発生した場合は再throwする。

  13. definition を新しい ペイント定義として、以下とする:

  14. paintDefinitionMap[name] に definition をセットする。

  15. タスクをキューに追加し、以下の手順を実行する:

    1. documentPaintDefinitionMap を関連する documentdocument paint definitions マップとする。

    2. documentDefinition を新しい 文書ペイント定義として、以下とする:

    3. documentPaintDefinitionMap[name] が 存在する場合、以下の手順を実行:

      1. existingDocumentDefinitionget documentPaintDefinitionMap[name] の結果とする。

      2. existingDocumentDefinition"invalid" なら、手順を中止する。

      3. existingDocumentDefinitiondocumentDefinition が等しくない場合(すなわち input propertiesinput argument syntaxesPaintRenderingContext2DSettingsオブジェクトが異なる場合)、次を実行する:

        documentPaintDefinitionMap[name] に "invalid" をセットする。

        同じクラスが異なる inputPropertiesinputArgumentspaintRenderingContext2DSettings で登録されたことをデバッグコンソールにエラーとして記録する。

    4. そうでなければ documentPaintDefinitionMap[name] に documentDefinition をセットする。

注: input properties のリストは一度だけ取得されるべきであり、クラスが動的にinput propertiesを変更することはできません。

注: 将来の仕様では、作者が異なる種類のRenderingContextを受け取れるようになる可能性があります。特に作者がWebGLレンダリングコンテキストを利用して3D効果を描画したい場合などです。WebGLコンテキストのセットアップには PaintSizeStylePropertyMap を入力として渡すなどの複雑さがあります。

5. ペイント記法

image/paint()

現時点で一つのエンジンのみ対応。

Firefox未対応Safari未対応Chrome65以上
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;

注: PaintRenderingContext2DCanvasRenderingContext2D APIのサブセットを実装します。 具体的には CanvasImageDataCanvasUserInterfaceCanvasTextCanvasTextDrawingStyles 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);
    }
});
ユーザーエージェントが指定されたwidthheightpaintRenderingContext2DSettingsPaintRenderingContext2Dオブジェクトを作成する場合、次の手順を実行しなければならない:
  1. 新しいPaintRenderingContext2Dを作成する。

  2. コンテキストのビットマップ寸法を設定し、出力ビットマップのサイズをwidthheightの丸め値にする。

  3. PaintRenderingContext2Dalphaフラグを paintRenderingContext2DSettingsalphaで設定する。

  4. 新しいPaintRenderingContext2Dを返す。

注: レンダリングコンテキストの初期状態はset bitmap dimensionsアルゴリズム内部で設定され、レンダリングコンテキストをデフォルト状態にリセットし、出力ビットマップもクリアされます。

6.1. CSSImageValueの描画

CanvasImageSource 型は、画像ソースとしてCSSImageValue 型も利用できるように拡張されます。

CanvasDrawImage ミックスインを利用するインターフェースについて:

注: これは最終的に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 は以下の通り定義されます。concreteObjectSizeconcrete object size です。通常 snappedConcreteObjectSizeconcreteObjectSize と同じですが、ユーザーエージェントがピクセル境界に合わせてサイズ調整することがあり、その場合は元のサイズからの比率で 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;
};
ユーザーエージェントが <paint()> 関数の box に適切なスタッキングレベル(関連するCSSプロパティで定義)で画像を描画したい場合、snappedConcreteObjectSizeを指定して以下の手順を実行しなければならない:
  1. paintFunction を描画対象の box 上の <paint()> 関数とする。

  2. namepaintFunction の第1引数とする。

  3. documentPaintDefinitionMap を関連する documentdocument paint definitions マップとする。

  4. documentPaintDefinitionMap[name] が 存在しない場合、画像出力は 無効な画像とし、以降の手順を中止する。

  5. documentDefinitionget documentPaintDefinitionMap[name] の結果とする。

  6. documentDefinition"invalid" の場合、画像出力は 無効な画像とし、以降の手順を中止する。

  7. inputArgumentSyntaxesdocumentDefinitioninput argument syntaxes とする。

  8. inputArgumentspaintFunction の "paint name" 引数以降の全引数の リストとする。

  9. inputArgumentsinputArgumentSyntaxes の登録文法と一致しない場合、画像出力は 無効な画像とし、以降の手順を中止する。

    このステップは以下のような場合に失敗します:
    // 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, 1px, 2px);
        }
    </style>
    <div class=example-1></div>
    <div class=example-2></div>
    <script>
        CSS.paintWorklet.addModule('paint.js');
    </script>
    

    example-1無効な画像となります。理由は "red" が登録文法に一致しないためです。

    example-2無効な画像となります。理由は引数が多すぎるためです。

  10. workletGlobalScopePaintWorkletGlobalScope のいずれかとして、§ 7.1 グローバルスコープ選択の規則に従う。

    ユーザーエージェントはこの時点で Worklet から worklet global scope を作成してもよい。

  11. invoke a paint callbacknameinputArgumentssnappedConcreteObjectSizeworkletGlobalScope で実行、必要なら 並列で。

    注: ユーザーエージェントが invoke a paint callback をスレッドで並列実行する場合、そのスレッドで使えるペイントワークレットグローバルスコープを選択するべきです。

ユーザーエージェントが paint callback を呼び出す場合、nameinputArgumentssnappedConcreteObjectSizeworkletGlobalScope を使い、以下の手順を実行しなければならない:
  1. paintDefinitionMapworkletGlobalScopepaint definitions マップとする。

  2. paintDefinitionMap[name] が 存在しない場合は、次の手順を実行:

    1. タスクをキューに追加し、次の手順を実行:

      1. documentPaintDefinitionMap を関連する documentdocument paint definitions マップとする。

      2. documentPaintDefinitionMap[name] に "invalid" をセットする。

      3. ユーザーエージェントは、全 PaintWorkletGlobalScope でクラスが登録されていない旨をデバッグコンソールにエラーとして記録するべき

    2. 画像出力は 無効な画像とし、以降の手順を中止する。

    注: これは、あるペイントワークレットグローバルスコープが registerPaint(name, paintCtor) を受け取らなかった場合を扱います(他のグローバルスコープが受け取った場合も)。他のグローバルスコープでペイントコールバックが成功しても、以降のフレームで draw a paint image が呼ばれると成功しません。

  3. definitionget paintDefinitionMap[name] の結果とする。

  4. paintClassInstanceMapworkletGlobalScopepaint class instances マップとする。

  5. paintInstanceget paintClassInstanceMap[|name|] の結果とする。paintInstance が null の場合は次の手順を実行:

    1. definitionconstructor valid flag が false の場合、画像出力は 無効な画像とし、以降の手順を中止する。

    2. paintCtordefinitionclass constructor とする。

    3. paintInstanceConstruct(paintCtor) の結果とする。

      もし construct が例外を投げた場合は、definitionconstructor valid flag を false にし、画像出力は 無効な画像とし、以降の手順を中止する。

    4. paintClassInstanceMap[name] に paintInstance をセットする。

  6. inputPropertiesdefinitioninput properties とする。

  7. styleMapStylePropertyMapReadOnly の新規インスタンスとして、inputProperties に列挙されるプロパティの computed value のみを設定する。

  8. renderingContextcreate a PaintRenderingContext2D object で次を渡して生成する:

    注: renderingContext はペイントの呼び出し間で再利用されません。つまり、呼び出し間で renderingContext に格納されたデータや状態はありません。例えば、コンテキストにクリップを設定しても、次回のペイントメソッド実行時に同じクリップは適用されません。

    注: また、ペイントメソッド終了後は renderingContext は実質的に「無効化」されます。作者コードが renderingContext を参照してメソッドを呼び出しても、現在の画像や以降の画像には影響しません。

  9. paintSizePaintSize の新規インスタンスとして snappedConcreteObjectSize の幅・高さで初期化する。

  10. この段階で、ユーザーエージェントは paintSizestyleMapinputArguments が前回と同じなら以前の呼び出しの画像を再利用してもよい。その場合は画像出力をキャッシュ画像とし、以降の手順を中止する。

    下記の例では、div-1div-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: 50px;
            height: 50px;
            background-image: paint(simple);
        }
        .div-2 {
            width: 100px;
            height: 100px;
    
            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>
    
  11. paintFunctionCallbackdefinitionpaint function とする。

  12. Invoke paintFunctionCallback を引数 «renderingContext, paintSize, styleMap, inputArguments» で、paintInstancecallback this value として呼び出す。

    もし paintFunctionCallback が適切な時間内に完了しない場合(ユーザーエージェントが「長時間実行スクリプト」と判断)、ユーザーエージェントはスクリプトを強制終了してもよい。画像出力は 無効な画像とし、以降の手順を中止する。

    注: ユーザーエージェントは、デバッグツールで作者にペイントクラスのコストを可視化したり、「応答しないスクリプト」ダイアログを表示するなどのツールを提供できます。

  13. 画像出力はメソッドに渡された renderingContext から生成する。

    もし例外が throw された場合、画像出力は 無効な画像とする。

注: 生成画像の内容はアクセシビリティ目的には設計されていません。作者は必要な情報を標準アクセシビリティAPIで提供してください。

7.1. グローバルスコープの選択

ユーザーエージェントが paint PaintWorkletGlobalScope を paint Workletglobal scopes リストから選択する必要がある場合、次を満たす必要がある:

注: これらの規則は、作者がグローバルオブジェクトやクラス上に状態を保存できることに依存しないようにするために存在します。詳細は 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: 36px;
    transition: --circle-color 1s;
    }

    #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: 200px;
    height: 200px;

    background-image:
    paint(arc, purple, 0.4turn, 0.8turn, 40px, 15px),
    paint(arc, blue, -20deg, 170deg, 30px, 20px),
    paint(arc, red, 45deg, 220deg, 50px, 10px);
}
</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) * 1px);

    border-image-source: paint(overdraw);
    border-image-slice: 0 fill;
    border-image-outset: calc(var(--border-width) * 1px);

    width: 200px;
    height: 200px;
}
</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公開以降の変更点:

適合性

文書の規約

適合性要件は、記述的な断定とRFC 2119の用語の組み合わせで表現されます。「MUST」、 「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、 「RECOMMENDED」、「MAY」、「OPTIONAL」というキーワードは、 本仕様の規範的部分においてRFC 2119で説明されている通りに解釈されます。 ただし、可読性のため、これらの語句は本仕様ではすべて大文字では記載されていません。

本仕様のテキストは、非規範的と明示されたセクション、例、および注記を除き、すべて規範的です。 [RFC2119]

本仕様における例は「例えば」で始まるか、class="example"として規範的テキストから区別されます。例えば:

これは有益な例の一例です。

情報提供的な注記は「注」と始まり、class="note"として規範的テキストから区別されます。例えば:

注:これは情報提供的な注記です。

助言事項は規範的セクションであり、特別な注意を促すために他の規範的テキストと区別され、<strong class="advisement">として示されます。例えば: UAはアクセシブルな代替手段を提供しなければならない。

適合クラス

本仕様への適合性は、3つの適合クラスについて定義されます:

スタイルシート
CSS スタイルシート
レンダラー
UAで、スタイルシートの意味を解釈し、 それらを使用した文書をレンダリングします。
オーサリングツール
UAで、スタイルシートを作成します。

スタイルシートが本仕様に適合するためには、 本モジュールで定義された構文を使用するすべての文が、汎用CSS文法および本モジュールで定義された各機能の個別文法に従って 有効である必要があります。

レンダラーが本仕様に適合するためには、 スタイルシートを適切な仕様で定義された通りに解釈することに加えて、 本仕様で定義されるすべての機能を正しく解析し、 文書をそれに従ってレンダリングできる必要があります。ただし、UAがデバイスの制限により文書を正しくレンダリングできない場合でも、 UAは非適合とみなされません。(例えば、UAはモノクロモニター上で色をレンダリングする必要はありません。)

オーサリングツールが本仕様に適合するためには、 本モジュールの汎用CSS文法および各機能の個別文法に従って構文的に正しいスタイルシートを書き、 本モジュールで記載されているスタイルシートの他の適合要件も満たす必要があります。

部分的な実装

著者が将来互換性のある構文解析ルールを活用して フォールバック値を指定できるように、CSSレンダラーは必ず、利用可能なサポートレベルがない atルール、プロパティ、プロパティ値、キーワード、その他の構文構成要素を無効(適切に無視)として扱う必要があります。特に、ユーザーエージェントは決して、 サポートされていないコンポーネント値を選択的に無視し、単一の複数値プロパティ宣言でサポートされる値のみを認めてはなりません。値のいずれかが無効とみなされた場合 (サポートされない値は必ず無効とみなされます)、CSSでは宣言全体が無視されます。

不安定・独自機能の実装

将来の安定したCSS機能と競合しないように、 CSSWGはベストプラクティスの遵守を推奨しています。 不安定な機能や 独自拡張の実装に関して。

非実験的な実装

仕様がCandidate Recommendation段階に到達すると、 非実験的な実装が可能となり、実装者は仕様どおり正しく実装できているCRレベルの機能について プレフィックスなしでリリースするべきです。

CSSの相互運用性を実装間で確立・維持するため、 CSSワーキンググループは非実験的なCSSレンダラーが実装レポート(必要に応じてその実装レポートで使用したテストケースも) をW3Cに提出することを要請しています。W3Cに提出されたテストケースはCSSワーキンググループによるレビューおよび修正の対象となります。

テストケースや実装レポートの提出に関する詳細は、 CSSワーキンググループのウェブサイト https://www.w3.org/Style/CSS/Test/ で確認できます。質問は public-css-testsuite@w3.org メーリングリストへお問い合わせください。

CR終了基準

本仕様をProposed Recommendationへ進めるには、 各機能について少なくとも2つの独立した相互運用可能な実装が必要です。各機能は異なる製品群によって実装されてもよく、 すべての機能が単一製品で実装される必要はありません。この基準のため、以下の用語を定義します:

独立
各実装は異なる当事者によって開発され、他の適格な実装で使用されたコードを共有、再利用、派生してはなりません。 本仕様の実装に影響しないコード部分はこの要件の対象外です。
相互運用可能
公式CSSテストスイートの該当テストケースに合格すること、 あるいは実装がWebブラウザでない場合は同等のテストに合格すること。テストスイートの各関連テストには同等のテストが作成されるべきであり、 そのようなUAで相互運用性を主張する場合は、同様にその同等テストを合格できる追加のUAが一つ以上必要です。これらの同等テストはピアレビューのために公開されなければなりません。
実装
ユーザーエージェントであり、以下を満たします:
  1. 仕様を実装していること。
  2. 一般公開されていること。実装は製品版、またはその他の公開版(ベータ版、プレビューリリース、「ナイトリービルド」など)でもよい。 製品未リリース版の場合は、少なくとも1ヶ月間該当機能を実装して安定性を示している必要があります。
  3. 実験的ではないこと(すなわち、テストスイート合格のためだけに設計されたバージョンで、今後通常利用される予定がないものではない)。

仕様は少なくとも6ヶ月間Candidate Recommendationとして維持されます。

索引

本仕様で定義される用語

参照で定義される用語

参考文献

規範的参考文献

[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2021年7月26日. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2021年12月3日. WD. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-DISPLAY-3]
Tab Atkins Jr.; Elika Etemad. CSS Display Module Level 3. 2021年9月3日. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-IMAGES-3]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Images Module Level 3. 2020年12月17日. CR. URL: https://www.w3.org/TR/css-images-3/
[CSS-IMAGES-4]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Image Values and Replaced Content Module Level 4. 2017年4月13日. WD. URL: https://www.w3.org/TR/css-images-4/
[CSS-PROPERTIES-VALUES-API-1]
Tab Atkins Jr.; 他. CSS Properties and Values API Level 1. 2020年10月13日. WD. URL: https://www.w3.org/TR/css-properties-values-api-1/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2019年7月16日. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TYPED-OM-1]
Shane Stephens; Tab Atkins Jr.; Naina Raisinghani. CSS Typed OM Level 1. 2018年4月10日. WD. URL: https://www.w3.org/TR/css-typed-om-1/
[CSS-UI-3]
Tantek Çelik; Florian Rivoal. CSS Basic User Interface Module Level 3 (CSS3 UI). 2018年6月21日. REC. URL: https://www.w3.org/TR/css-ui-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2021年10月16日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS-VARIABLES-1]
Tab Atkins Jr.. CSS Custom Properties for Cascading Variables Module Level 1. 2021年11月11日. CR. URL: https://www.w3.org/TR/css-variables-1/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021年8月26日. WD. URL: https://www.w3.org/TR/cssom-1/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM View Module. 2016年3月17日. WD. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 他. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL索引

partial namespace CSS {
    [SameObject] readonly attribute Worklet paintWorklet;
};

[Global=(Worklet,PaintWorklet),Exposed=PaintWorklet]
interface PaintWorkletGlobalScope : WorkletGlobalScope {
    undefined registerPaint(DOMString name, VoidFunction paintCtor);
    readonly attribute unrestricted double devicePixelRatio;
};

dictionary PaintRenderingContext2DSettings {
    boolean alpha = true;
};

[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;

[Exposed=PaintWorklet]
interface PaintSize {
    readonly attribute double width;
    readonly attribute double height;
};