こんにちは、福岡オフィスでエンジニアをしている petamoriken です。 ECMAScript と DOM API が好きで、気づいたらいつも仕様を眺めています。よろしくお願いします。
この記事では画像に関する新しい DOM API を紹介します。一般的に DOM API は二つのブラウザによる実装があれば安定していると言われていますが、プロダクトなどに採用する際には十分気をつけてください。また将来的に仕様が変更される可能性があることにも留意してください。
HTMLImageElement#decode()
明示的に画像のデコードをさせるメソッドです。Promise<void>
を返します。
実は画像の load イベント完了後では画像の大きさを取得することは出来ますが、まだデコード処理はなされていません。今まで明示的にデコードさせたかったらこのようなコードを書いていたと思います。
この方法には問題があり、メインスレッドでデコード処理をしてしまうためブラウザを重くしてしまう可能性があります。
HTMLImageElement#decode()
は仕様に in parellel と書いてあるようにメインスレッドを固めることなくデコードさせる事ができます。
OffscreenCanvas
HTMLCanvasElement
と違い、DOM に依存していない Canvas です。 OffscreenCanvasRenderingContext2D
がテキストの描画をすることが出来ない代わりに、Transferable
で Dedicated Worker (非 Shared Worker) 内で描画処理をさせることが出来ます。もちろん WebGL も扱えます。
HTMLCanvasElement#transferControlToOffscreen()
を呼ぶことで HTMLCanvasElement
から OffscreenCanvas
を作り、描画処理を委譲させることが出来ます。これによって生成された OffscreenCanvas
を Worker
内で描画させることによって、メインスレッドを固めることなくアニメーションをさせることが出来ます。
詳しくは二年前に弊社のエンジニアである Ragg が書いた記事を御覧ください。
ただし当時は Window
から requestAnimationFrame
のタイミングを postMessage
で送らないといけませんでしたが、最新仕様では DedicatedWorkerGlobalScope に requestAnimationFrame が生えているので単にそちらを使えばいいようです。
アニメーションをさせる必要がなく、一枚の画像を描画したい場合は今までと同じように OffscreenCanvas#convertToBlob(options)
で Blob
を作り、URL.createObjectURL(blob)
で Blob URL を作って Worker
から Window
に送ることも出来ますが、OffscreenCanvas#transferToImageBitmap()
を呼ぶと後述する ImageBitmap
をゼロコピーで作ることが出来ます。
ImageBitmap
一枚の画像を扱うことが出来るインターフェースです。なんと Transferable
かつ Serializable
です。すなわち Window
と Worker
で送り合うことも出来れば、IndexedDB に格納することも出来ます。
ImageBitmap
は HTMLImageElement#decode()
を呼んだときのように、事前に描画のための処理を済ませています。そして CanvasImageSource の一つであり、CanvasDrawImage#drawImage
を使って描画することが出来ます。ちなみに CanvasImageSource
について完全に余談ですが、最近 CSS Typed OM の中で CSSImageValue が CanvasImageSource の一つであると拡張されていたりします。
createImageBitmap(image [, options])
createImageBitmap(image, sx, sy, sw, sh [, options])
ImageBitmap
は前述した通り OffscreenCanvas
からも作ることが出来ますが、Window
と Worker
内で使える createImageBitmap
を使っても生成することが出来ます。第一引数には先程の CanvasImageSource
の他に、Blob
と ImageData
を入れることが出来ます。後述する ImageBitmapRenderingContext
を使うとわざわざ Blob
を URL.createObjectURL(blob)
に入れずに HTMLCanvasElement
を使って描画することが出来て便利です。また options 内で生成する ImageBitmap
の大きさを変更させることが出来ます。
残念ながら Firefox でバグがあり sx, sy, sw, sh を省略した場合に options を受け入れなかったり、リサイズ系の options を無視してしまったりすると報告されています。
ImageBitmapRenderingContext
HTMLCanvasElement#getContext("bitmaprenderer", options)
で作ることが出来る RenderingContext です。ただ一つ transferFromImageBitmap(imageBitmap)
というメソッドのみを持ちます。これは ImageBitmap
から画像をゼロコピーで受け取ります。譲渡された ImageBitmap
は空になります。
このときの HTMLCanvasElement
の見た目ですが、Chrome で試したところ HTMLImageElement
と同じような感じになってそうです。ただし HTMLCanvasElement
の width, height にはデフォルト値 300, 150 が入っていますので、ImageBitmap
と同じサイズの画像を表示させてい場合は譲渡する前にそのサイズを HTMLCanvasElement
に渡す必要がありそうです。ちなみに描画後に width, height を変更させても HTMLCanvasElement
の描画がリセットされることはなさそうです。
残念ながら Chrome でバグがあり、ImageBitmapRenderingContext を持つ HTMLCanvasElement で toDataURL/toBlob を呼ぶと同じサイズの透明な画像を生成してしまうことが報告されています。特に役に立たないと思われますがこれを判定するコードが以下になります。
いかがだったでしょうか。このあたりの実装ですが主に Chrome と Firefox のみとなっていて更に実装されていてもまだバグがあるような状態ですので、もうしばらくはプロダクト採用はできなそうだと思っています。
最近 Edge が Chromium ベース実装になると発表があり、見かけ上の実装されるブラウザは多くなりそうではありますが多様性の観点からとても残念に思いますね。Edge が他のブラウザを差し置いて Async Functions を真っ先に実装して、とても勢いがあった時期を覚えているだけに悲しい気持ちになります。
ピクシブ株式会社では Web の行く末をより良いものとするために積極的に仕様策定の議論に参加するエンジニアを募集しています!
福岡: 中途採用 / インターンシップ(学生)
東京: インターンシップ(学生)