メインスレッドの「呪縛」から脱却せよ:OffscreenCanvasで描画のボトルネックを叩き潰す
フロントエンドの現場で「レンダリングが重い」という相談を受けるとき、その原因の8割はメインスレッドの占有にあります。
ブラウザのメインスレッドは、いわば「何でも屋」の店長です。JSの実行、DOM操作、スタイル計算、レイアウト、そして描画まで、すべてをたった一人でこなしている。ここに重いCanvas描画処理を放り込めばどうなるか? 当然、スクロールはカクつき、クリック反応は遅延し、ユーザー体験(UX)は地の底まで落ちます。
今回は、この「店長」の負担を大幅に軽減するOffscreenCanvas APIという強力な武器について、現場の知見を交えて解説します。
—
ブラウザの裏側で何が起きているのか?
通常、Canvasへの描画はメインスレッドで行われます。`requestAnimationFrame` をループさせて描画しているとき、ブラウザは「JSの実行」と「描画指示」を同じスレッドで交互に処理しています。
しかし、`OffscreenCanvas`を使うと、「描画処理」をWeb Workerという別スレッドへ丸投げできるようになります。
これにより、メインスレッドは「ユーザーのインタラクション(クリックやスクロール)」に専念し、Workerは「計算と描画」に専念するという、理想的な並列処理が実現します。ブラウザの内部的には、Workerで生成された描画コマンドが、メインスレッドを介さずにCompositorスレッドへ送られ、GPUへと効率的に渡されるパイプラインが構築されるのです。
—
実践:Workerを使ったOffscreenCanvasの構築
まずは、現場でそのまま使える最小構成のパターンを紹介します。ポイントは、メインスレッドから `transferControlToOffscreen()` を使って描画権限をWorkerに譲渡することです。
1. メインスレッド側(main.js)
DOM要素をWorkerに渡すのではなく、Canvasの制御権そのものを移譲します。
// メインスレッド
const canvas = document.querySelector(‘#my-canvas’);
// 描画権限をWorkerへ移譲(一度移譲するとメインスレッドでは描画不可になるので注意!)
const offscreen = canvas.transferControlToOffscreen();
// Workerを生成し、offscreenインスタンスを転送する
const worker = new Worker(‘worker.js’);
worker.postMessage({ canvas: offscreen }, [offscreen]);
2. Worker側(worker.js)
Worker側では、受け取ったCanvasを普通のCanvasのように扱えます。
// ワーカー内
self.onmessage = (e) => {
const { canvas } = e.data;
const ctx = canvas.getContext(‘2d’);
function render() {
// ここで重い描画処理を実行しても、メインスレッドは止まらない!
ctx.fillStyle = `hsl(${Math.random() 360}, 50%, 50%)`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 描画ループを維持
requestAnimationFrame(render);
}
render();
};
—
現場で陥りやすい罠と対策
この技術を導入する際、シニアとして知っておいてほしい「ハマりどころ」が3つあります。
1. 一度移譲したら戻れない
`transferControlToOffscreen()` を実行した時点で、メインスレッド側では `canvas.getContext` が呼べなくなります。UIのレイアウト変更などでCanvasのサイズを変えたい場合は、Worker側にメッセージを送ってリサイズ処理をさせる設計が必要です。
2. DOMへのアクセスは不可
WorkerはDOMに直接触れません。「Canvas上のクリック座標を取得したい」という場合は、メインスレッドで `addEventListener` を受け取り、そのイベントを `postMessage` でWorkerに中継する必要があります。
3. ブラウザ互換性の壁
現在、主要ブラウザ(Chrome, Firefox, Edge)は概ね対応していますが、Safariは長らく追従が遅れていました。プロダクション投入前には、必ず `if (‘transferControlToOffscreen’ in canvas)` で機能判定を行い、非対応環境用のフォールバック(メインスレッド描画)を用意するのがプロの矜持です。
—
最後に:パフォーマンス向上は「非同期」の哲学から
OffscreenCanvasは魔法の杖ではありません。単に描画処理を移動させただけで、アルゴリズム自体が非効率であれば、結局Worker側のCPU使用率が跳ね上がり、バッテリー消費を招きます。
しかし、「メインスレッドを空けておく」という設計思想は、現代の複雑なWebアプリケーションにおいて最強の守護神です。重いデータ可視化、複雑なアニメーション、あるいは画像処理などを実装する際は、ぜひこの「Worker分離」を真っ先に検討してみてください。
ブラウザの仕組みを理解し、スレッドの負荷を適切に分散させる。それができれば、あなたの作るUIは、どんなにリッチなコンテンツを詰め込んでも「サクサク動く」はずです。さあ、まずは今のプロジェクトの描画処理をWorkerへ追い出すところから始めてみましょう。

コメント