WebGL キャンバスで HTML をレンダリングすると、ライブ DOM サブツリーを動的な GPU テクスチャとして使用したり、ライブ DOM にシェーダー エフェクトを適用したり、インタラクティブな HTML を 3D シーンに統合したりできます。
このガイドでは、HTML を WebGL キャンバスにレンダリングする方法について説明します。HTML-in-Canvas の詳細については、API の概要をご覧ください。
WebGL キャンバスで HTML をレンダリングするには、次の手順が必要です。
- キャンバスを準備します。適切な属性を宣言的に設定して、HTML をキャンバスにレンダリングする必要があることをブラウザに知らせます。この設定は、2D、WebGL、WebGPU のコンテキストで共通です。キャンバスを設定する方法と、HTML 要素を個別に描画するように宣言する方法を学習します。
- HTML をテクスチャとして適用します。
- GPU テクスチャ ストレージを事前に割り当てます。
- DOM スナップショットをテクスチャとしてアップロードします。
- DOM とピクセルを同期します。
HTML 要素を WebGL テクスチャとして描画する
WebGL では、HTML 要素は texElementSubImage2D() を使用して 2D テクスチャとして GPU にアップロードされます。
テクスチャ ストレージを事前に割り当てる
バッキング メモリを自動的に管理する 2D キャンバスとは異なり、WebGL コンテキストでは明示的なメモリ管理が必要です。
DOM ピクセルをアップロードする前に、GPU メモリを事前に割り当てる必要があります。これにより、ブラウザは高負荷の GPU メモリの再割り当てをトリガーすることなく、サブイメージの更新を高速に実行できます。
選択したサイズ パラメータと null に設定された srcData を使用して、次のようにバッファを事前に割り当てます。
// Only allocate texture backing once
if (!textureBufferAllocated) {
// Pre-allocate texture backing with pre-determined size.
gl.texImage2D(
/* target */ gl.TEXTURE_2D,
/* level */ 0,
/* internalformat */ gl.RGBA8,
/* width */ myElementWidth,
/* height */ myElementHeight,
/* border */ 0,
/* format */ gl.RGBA,
/* type */ gl.UNSIGNED_BYTE,
/* srcData */ null
);
textureBufferAllocated = true;
}
テクスチャを HTML 要素の自然なレイアウト サイズに合わせるには、canvas.getElementImageDefaultSize() を使用して、キャンバス グリッド ピクセル単位でデフォルトのサイズを取得します。
const { width, height } = canvas.getElementImageDefaultSize(myElement);
// Pre-allocate texture backing. Only do this once, or whenever the element
// changes size.
gl.texImage2D(
/* target */ gl.TEXTURE_2D,
/* level */ 0,
/* internalformat */ gl.RGBA8,
/* width */ Math.ceil(width),
/* height */ Math.ceil(height),
/* border */ 0,
/* format */ gl.RGBA,
/* type */ gl.UNSIGNED_BYTE,
/* srcData */ null
);
DOM スナップショットをテクスチャとしてアップロードする
次のステップでは、レンダリング ループまたは paint リスナー内で、HTML 要素をテクスチャにアップロードします。
canvas.onpaint = () => {
// 1. Pre-allocate texture backing first
// ...
// 2. Upload HTML snapshot into the WebGL texture
gl.texElementSubImage2D(
/* target */ gl.TEXTURE_2D,
/* level */ 0,
/* xoffset */ 0,
/* yoffset */ 0,
/* element */ myElement // or an ElementImage from `captureElementImage()`
);
// ...
};
ジオメトリを同期する
2D コンテキストとは異なり、3D コンテキストでは、ブラウザはテクスチャが画面のどこに表示されるかを自動的に推測できません。DOM ヒットテストとユーザー補助の境界ボックスを 3D メッシュに手動で合わせる必要があります。
これを行うには、要素の境界ボックスをレンダリングされたキャンバスのピクセル位置にマッピングする DOMMatrix を作成し、updateElementGeometry() を使用して適用します。
canvas.addEventListener("paint", () => {
// 1. Upload fresh DOM snapshot to GPU texture
gl.bindTexture(gl.TEXTURE_2D, myTexture);
gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, myElement);
// 2. Draw your 3D scene
draw3DScene();
// 3. Construct the CSS-pixel transform mapping the element's
// border box to its drawn position on the canvas as needed
// (for example: scale and translate)
const canvasTransform = new DOMMatrix();
// scale X
canvasTransform.a = canvas.clientWidth / myElement.offsetWidth;
// scale Y
canvasTransform.d = canvas.clientHeight / myElement.offsetHeight;
// translate X (CSS px)
canvasTransform.e = 50;
// translate Y (CSS px)
canvasTransform.f = 100;
// 4. Update DOM position, accessibility focus rings, and pointer hit-testing
canvas.updateElementGeometry(myElement, {
canvasTransform: canvasTransform,
});
});
次のステップ
- HTML-in-Canvas の背後にある主要な技術コンセプトを理解します。
- 2D コンテキストで HTML をレンダリングする方法を確認する。
- WebGPU Canvas で HTML をレンダリングする方法を確認する。
- フォールバック戦略について確認する。
- GitHub で Issue を作成して、フィードバックをお寄せください。
- コミュニティによる HTML-in-Canvas のデモ コレクションをご覧ください。