1. alpha_350

    No comment

    alpha_350
Changes in body
Source | HTML | Preview

Chrome web workerを使ってcanvasで絵を描く。

※まだChrome69から実装された機能で他のブラウザでは動きません。

動機

描画の高速化を調べて色々ググっていると、MDNにOffscreenCanvasなるメソッドの記事を見つけて、WebWorkerを使えば何とかなるんじゃないかと考えていたのでこの記事はまさにど真ん中の記事でした。

早速実装にとりかかるとWebWorker初実装の私には、コールバックの嵐に見事に心が砕けてしまいました。
そこに救世主・・・・Comlinkが現れました。

Comlinkとは

コールバックやらイベントやらで呼ぶのではなく、その辺をよきに計らってくれてPromiseを返してくれるライブラリです。
コールバックよりは、async-awaitの方が読みやすいかなと使ってみることに。
githubを見てみると「Support OffscreenCanvas (fixes #165)」の文字が、これは期待度が高まります。

早速インストール

npm i comlink
main.js
import * as Comlink from "comlink";
class Main {
    constructor() {
      this.createPng = Comlink.proxy(new Worker("worker.js"));
    }
    async createPng(){
        const instance = await new this.createPng();
        const canvas = document.createElement('canvas');
        const offscreen = canvas.transferControlToOffscreen();
        const url = await instance.createPng(offscreen);
        return url;
    }
    async show() {
       let imageData = await this.createPng(history, date);
       // 実際の描画処理。
    }

}
woker.js
import * as Comlink from "comlink";
class MyClass {
 async createPng(offscreen) {
    let ctx = offscreen.getContext("2d");
    // ・・・描画処理
    // 完成したらpngURLに変換
    const blob = await offscreen.convertToBlob();
    const p = new Promise ((resove,reason) => {
      const reader = new FileReader();
      reader.onload = () =>{
        let dataUrl = reader.result;
        let base64 = dataUrl.split(',')[1];
        resove("data:image/png;base64, "+base64);

      }; 
      reader.readAsDataURL(blob);
    });
    return await p;
 }
}
Comlink.expose(MyClass, self);
woker.js
import * as Comlink from "comlink";
class MyClass {
 async createPng(offscreen) {
    let ctx = offscreen.getContext("2d");
    // ・・・描画処理
    // 完成したらpngURLに変換
  //toDataURLメソッドがないので、convertToBlob()で代用。 
    const blob = await offscreen.convertToBlob();
    const p = new Promise ((resove,reason) => {
      const reader = new FileReader();
      reader.onload = () =>{
        let dataUrl = reader.result;
        let base64 = dataUrl.split(',')[1];
        resove("data:image/png;base64, "+base64);

      }; 
      reader.readAsDataURL(blob);
    });
    return await p;
 }
}
Comlink.expose(MyClass, self);

動かない。全く動かない。

 インストールしたモジュールをみると、「Support OffscreenCanvas (fixes #165)」で追加された実装がないことが分かりました。
しかたなく、githubからzipを持ってきてファイルを置き換えることに。

動いた!

最初は、canvas と offscreen を使いまわす予定でしたが、ループ処理を入れると、offscreenのところでエラーが発生するので、worker側を呼び出す前に毎回作成するように変更しました。

まとめ

Comlinkはいいですね。