drawImageによる画像の挿入
drawImageメソッドを用いると, canvas要素に任意のラスタ(ベクタ)画像を描画することができます.
CanvasRenderingContext2D.drawImage()
既存の画像を描画する. HTMLImageElement/HTMLVideoElement/HTMLCanvasElement/ImageBitmapが描画対象. なお, GIFアニメーションや動画については, 特定のフレーム(大抵は先頭フレーム)がスナップショット的に描画される. なお, WebKitでは, アニメーションGIF/PNG画像を書き込むタイミングによって出力されるフレームが変化する.
CanvasRenderingContext2D.drawImageFromRect()
互換性のために残されているdrawImageメソッドのエイリアス(WebKit環境のみ).
なお, CSS背景画像等をcanvas要素に直接描画することはできません.
描画可能な画像のフォーマット
基本的にブラウザがサポートしている(img要素で表示可能な)画像・動画形式であればcanvas要素に描画できますが, ブラウザ互換性を鑑みると, PNG, JPEGのいずれかを利用するのが無難です.
エンジン別画像フォーマット対応状況(wikipedia より抜粋)
format mime type Gecko Blink EdgeHTML WebKit 備考
JPEG image/jpeg ○ ○ ○ ○ 非可逆
GIF image/gif ○ ○ ○ ○ 可逆, 色数限定, 単色透明可, アニメーション可
PNG image/png ○ ○ ○ ○ 可逆, 透明可
APNG image/png ○ △ △ ○ Blink/EdgeHTML環境ではアニメーション不可
SVG image/svg+xml ○ ○ ○ ○ ベクタ形式, スクリプトによる生成容易
BMP image/bmp ○ ○ ○ ○ 未/低圧縮, 透明可, スクリプトによる生成容易
WEBP image/webp ׆ ○ × △† 可逆・非可逆, 透明可, アニメーション可
JPEG XR image/vnd.ms-photo × × ○ ×
JPEG2000 image/jp2 × × × △ Safariのみ?
TIFF image/tiff × × ○ ○ マルチページ
†将来的なサポートを表明.
なお, 現在WEB環境では色空間としてsRGB(ないしはlinearRGB)の利用を前提としています.
画像形式のサポート判定
ブラウザ環境が指定した画像形式の表示をサポートするかどうかは, img要素に実際に画像を読み込ませてエラーとならないかどうかで判定します. そのために, 1px平方の画像をデータURIスキーム形式に変換したものを用いると便利です.
このブラウザはWEBP形式をサポート .
画像挿入の3パターン
画像挿入に用いるdrawImageメソッドは渡した位置パラメータの数で座標指定 矩形指定 トリミング指定 の3つに動作変化します.
drawImage(image, dx, dy)
(座標指定)画像をカンバスの指定した位置を基準に描画したい場合に用います.
drawImage(image, dx, dy, dw, dh)
(矩形指定)画像をカンバスの矩形領域に描画したい場合に用います.
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
(トリミング指定)画像をトリミングしてカンバスに描画する場合に用います.
引数imageにはHTMLImageElement(Imageオブジェクト), HTMLCanvasElement, HTMLVideoElement, ImageBitmapの何れかを指定します.
下はimg要素で読み込んだ画像ファイルです. これをカンバス要素に描画してみましょう.
座標指定
位置パラメータが2つの場合の動作で, 指定した座標(dx, dy)を起点に画像が描画されます. canvasサイズからはみ出た部分は無視されます.
矩形指定
位置パラメータが4つの場合の動作で, 指定した矩形(dx, dy, dw, dh)に従って画像が伸縮されます.
またdw, dh値に負の値を指定することで, 矩形の基準(通常は左上)を右下等に変更することが出来ます.
この動作は比較的新しいもので, かつてはエラーとなったり画像が反転するなどの動作の揺れがありました.
トリミング指定
位置パラメータが8つの場合の動作で, 2つの矩形パラメータに従い画像を指定した矩形範囲(sx, sy, sw, sh)で切り取り, その内容をカンバスの(dx, dy, dw, dh)領域に描画します. 画像の切り出し, 画像の分離(スプライト画像の切り出し)等に用います.
画像描画時のマッピング
sx, sy, sw, shで指定した矩形範囲が元画像の範囲から外れていた場合やsw, sh値が0の場合, かつてはエラーを発生する仕様でしたが, 現在では廃止されています.
画像挿入に関わるテクニック
画像の挿入に際して覚えておくと便利なテクニックについて示します.
座標軸変換による画像の変形
drawImageメソッドでは予め座標軸を変形しておくことで画像を変形することができます. 下の例は画像を左右反転させた例です.
canvas要素の参照
drawImageメソッドでは画像の参照先としてcanvas要素が使えます. 例えばアニメーション処理を行う際に前景と背景とを別々に描画し, 最後に一つにまとめるといった構成を採ることも可能です.
また自分自身を書き込むことも出来ます.
画像サイズを取得する
img要素における画像サイズにおいてもcanvas要素と同じく画像そのものサイズと画像の描画サイズの2つが定義されています.
HTMLImageElement.width/height
画像の見た目のサイズを取得する. CSSによるサイズが指定されていない場合, スクリーンに描画されていない場合は画像そのものサイズを返す.
HTMLImageElement.naturalWidth/naturalHeight
img要素そのもののサイズを取得する. 通常画像のサイズに合わせられるが, srcset属性が有効な場合は選択された画素密度の分除算される.
この値を用いるとアスペクト比(横縦比)を維持しながら画像を描画できます.
また, 画像の加工を目的としているのであればnaturalWidth/naturalHeight値をcanvas要素に転写します.
画像ロードとdrawImageメソッドの実行順
本項をより良く理解するにはJavaScriptが採用している並行処理モデル(イベントループとメッセージキュー)を知っておく必要があります.
外部画像を読み込む場合, HTML文書に定義されているimg要素を参照する 動的にHTMLImageElement(Image)オブジェクトを生成して参照する ImageBitmapを生成して参照する(詳しくは後述します) の3つの方法が存在します. この時, srcプロパティを変更した際に行われる画像ファイルの読込・分析† はメイン処理とは別のスレッドで行われる ため, drawImageメソッドを実行したタイミングによってはまだ画像のロードが終わっていないことがあります.
画像の読み込みと画像の取得
†img要素の画像の読み込み開始にDOMツリーへの所属の有無は関係ありません.
従ってこの状態のままdrawImageメソッドを実行してしまうと画像の書き込みに失敗します. 下の例ではimg要素に画像の参照先を設定した直後にdrawImageメソッドを実行している ため, 画像の描画成否が不定 となります.
再現性に乏しいため一見厄介ですが, この問題を解決するにはimg要素の持つonloadイベントでカンバスの描画処理を行うようにします.
しかしこの方法をもってしても, srcプロパティとリスナ関数の登録処理の順番を変えると稀に描画処理に失敗します.
画像読み込みにおけるトラブルは大抵この問題に行き着きます.
画像ロード待ちのバリエーション
img要素のロード待ちを実現するコードには幾つかバリエーションがあります.
onload属性の利用
img要素のonload属性にスクリプトを記述する方法があります.
completeプロパティの利用
img要素のcompleteプロパティを使って画像読み込みの完了を確認する方法です.
window環境での恩恵は少ないものの, WSH環境でブラウザ外部からimg要素の画像読み込みを待つケースに於いて有効です.
画像読み込み失敗への対処
ネットワークを介した画像読み込みは常に成功するとは限りません. そのため正常系の処理をloadイベントに記述すると共に, 異常系の処理をerrorイベントに記述することでより堅牢なスクリプトになります.
複数の画像ソースを利用する場合
canvas要素に読み込む画像が複数にわたる場合はもう少し細工を施す必要があります. 読み込みが完了した画像を数え, 全ての画像が読み込まれたことを確認してから描画処理を開始するようにします. HTMLImageElement.completeプロパティは画像を読み込んでいない際にもtrue
を返す ためこの用途においては適切ではありません.
必要な画像データをいわゆるスプライト画像として一つのファイルにまとめておく方法もあります.
decodeメソッドを用いた画像の読み込み待ち
Imageオブジェクトに新たに追加されたdecode
メソッドを用いると, これまでload
イベントに記述していた処理を, Promiseで書き換えることが可能です. つまり, 複数画像の読み込みもよりスマートに記述することが可能となります.
Image.decode() 画像の読み込み完了を待つPromiseを返します.
複数画像の読み込みの例はImageBitmapの項を参照して下さい.
画像表示が制限された環境での動作
プライバシー設定等により画像の表示が制限されている環境ではimg要素(及びImage / HTMLImageElementオブジェクト)が適切なイベント(load / error)を発生しません. この場合, 下記に示す同期的な画像読み込みもしくはImageBitmapオブジェクトを使った方法を用います.
補足)同期的な画像読み込み
画像データの読み込みにHTMLImageElement/ImageBitmapではなく独自のデコーダを利用する場合は, 次のようにすると画像をcanvas要素に同期的に書き込むことが可能です.
XMLHttpRequestを用いて画像データ(バイナリ)を同期的に取得する(open
メソッドの第3引数にtrue
を指定する).
上で得たバイナリからビットマップデータを復元する.
ImageDataオブジェクトを介してビットマップデータをcanvas要素に転写する(画像ソースの生成).
必要に応じてdrawImage
メソッドを使って上記canvas要素を別のcanvas要素に描画する.
この方法を用いるとコードが単純になる一方でスクリプトの応答性が損なわれるため, コードの実行状況がブラウザの動作に影響しないworker環境での利用が推奨されます.
decoding属性による読み込みの制御
img要素に新たに追加されたdecoding
属性を用いると, 画像の読み込みを同期的に行う(sync)か非同期的に行う(async)かを指定できます.
ソース画像の自動選択
img要素に表示される画像の候補は単独とは限りません. picture要素やsrcset属性と組み合わされているimg要素をcanvas要素に転写するには工夫が必要です.
picture要素とcanvas要素
picture要素はimg要素と組み合わせて, 環境ごとに最適な画像ファイルだけ を読み込み表示します. 例えばWEBP形式をサポートする環境ではWEBP形式の画像を, そうでない環境ではPNG形式の画像を表示すると言ったことが可能です.
picture要素をcanvas要素に描画するには, picture要素配下のimg要素をcanvas要素に描画するようにします.
なお, どの画像が実際に使われたのかを判定するには下記currentSrc
プロパティを用います.
srcset属性とcanvas要素
img要素のsrcset属性を用いると, スクリーンのピクセル密度(及びズーム倍率)に応じた画像の選択が可能となります.
このようなimg要素を直接canvas要素に描画するのは得策ではありません. なぜなら画像本来のサイズを表すnaturalWidth/naturalHeight値がsrcset属性に記述された密度値(2x等)の影響を受ける からです† . そこで, HTMLImageElementオブジェクトのcurrentSrc
プロパティから現在選択されている画像URLを別のImageオブジェクトに読み込み, その上でcanvas要素に書き戻します.
†例えば密度値2xの環境下でサイズ400×400の画像が選択されていた場合, naturalWidth / naturalHeight値は200 / 200(画像サイズ÷密度値)となります.
HTMLImageElement.currentSrc img要素に現在表示している画像ソースの実際のURLを取得する
画像の描画品質
drawImageメソッドによるラスタ画像の描画には注意すべき点があります.
アンチエイリアスの設定
imageSmoothingEnabled
プロパティを用いるとラスタ画像をカンバスに描画する際の品質(アンチエイリアスの有無・品質)を設定できます. ドット絵等の低解像度の画像データを拡大する場合に利用します.
CanvasRenderingContext2D.imageSmoothingEnabled
画像を描画する際の品質を指定する. falseでアンチエイリアス(平滑化)を無効とする. (図形描画等には影響しない)
CanvasRenderingContext2D.imageSmoothingQuality
アンチエイリアスの品質を指定する. (low/medium/high)
画像縮小時の品質改善
imageSmoothingEnabled
プロパティをtrue
に設定していてもdrawImageメソッドで大きな画像を極端に縮小すると, CSSで縮小した場合と比べジャギー(色境界のギザギザ)が目立つことがあります.
Webkit系ブラウザ(Safari)では発生しません.
CSSによる画像の縮小例
この問題は画像の縮小アルゴリズム(単純なバイリニア補間)によるもので, サイズ1/2未満の縮小を行った際に細かい部分のピクセルデータが欠落することで発生しています. 縮小前に広範囲のピクセルデータがブレンドされるようにすると改善します.
逐次縮小によるもの
一度に目的のサイズに縮小するのではなく, 作業を何回かに分割して実行します.
フィルタによるもの
元画像を一旦canvas要素でぼかしてから縮小します. ぼかし半径は控えめに設定すると上手く行くようです.
画像のモザイク化
ソース画像をモザイク化するには様々な方法がありますが, imageSmoothingEnabledが使えるなら一旦小さなcanvas要素に描画した内容を引き伸ばすだけで実現できます.
CSSを用いた画像のモザイク拡大
CSSのimage-renderingプロパティを指定することで, canvas画像をモザイク拡大する方法もあります. canvas要素側の処理が単純になる反面, 歴史的経緯から指定するスタイル値がブラウザ毎にバラバラという難点があります. またInternet Explorer9以降では-ms-interpolation-modeプロパティが効きません.
CSS4におけるpixelatedとcrisp-edgesの違いは前者がドットをそのまま(nearest-neighbor法による)拡大するのに対し, 後者はピクセルを拡大する際に色境界を滑らかにする可能性がある 点です. しかしcrisp-edgesを指定した際の拡大アルゴリズムの指定はされておらず, pixelatedのように表示しても良いことになっており互換性の観点からは扱いにくい値と言えます.
image-renderingプロパティにおけるの動作の違い(イメージと実動作)
元画像 pixelated crisp-edges
画像読み込みの応用
ここまでは単純なURLを用いた画像読み込みを行いましたが, これを応用すると次のような場面においても画像を取得可能です.
Ajaxによる画像データの取得
input[type=file]による画像データの取得
ファイルドロップによる画像データの取得
コピーペースト操作による画像データの取得
Ajax画像の描画
XMLHttpRequestオブジェクトを使い, 画像データをバイナリデータ(Blobオブジェクト)として取得する方法です. 得られたBlobオブジェクトはURL.createObjectURLメソッドでBlobデータスキームに変換することで, img要素に渡すことが可能です.
一旦画像ファイルをBlob(ArrayBuffer)として扱う方法は, (ライブラリを使って)GIFアニメーションをフレーム画像に分割すると言った場合に用いられます.
なお, WEBサーバーから画像データを取得するのにFetch APIを用いる方法もあります. 本項の最後にサンプルコードを記載しています.
ローカル画像の描画
input[type=file]要素で画像ファイルを取得する方法です. 得られたFileオブジェクトはBlobオブジェクトでもあるため, 画像ファイルが得られた後は先ほどと同じです.
ドロップ画像の描画
dropイベントから画像ファイルオブジェクトを取得する方法です. 画像ファイルが得られた後は先ほどと同じです.
いずれも何らかの手段で画像データに相当するBlob(File)を得る URL.createObjectURLメソッドを用いてBlobをimg要素に表示する img要素には予めonloadイベントにcanvas要素への描画処理を記述しておく 描画処理が完了したらURL.revokeObjectURLメソッドでBlobオブジェクトを開放する と言った手順をとっている点に着目しましょう.
補足)誤ドロップへの対処
ドロップ操作を採用する場合, ユーザビリティの観点から誤ドロップによる意図しないページ遷移への対処が必要です.
クリップボード画像の描画
WEBページ上の画像を右クリックした際に表示される「画像のコピー」を使ってコピーした画像を, ペースト操作(キーボード操作を含む)でcanvas要素に画像を描画する事が出来ます. 実現方法には概ねpasteイベントを用いる contenteditable属性を用いる の2つがあります.
なお, クリップボード内の画像の形式や, クリップした環境等によって画像が描画されないケースもあります. また, 異なるWEBページに表示されている画像を描画した場合, 後述するorigin-cleanフラグによって利用可能な機能が制限されます.
コピー用の画像(右クリックでコピーして以下のコードでペーストしてみましょう)
pasteイベントを用いるもの
pasteイベントを使って, クリップボードの中身を参照する方法です. canvas要素そのものはpasteイベントを発生させないので, canvas要素を囲む何らかのHTML要素を用意する必要があります. また, そのままではペースト可能な範囲が不明瞭なので何らかの誘導が必要となるでしょう. 動作する環境はChromeに限られます.
MutationObserverによるもの
contenteditable属性をtrueとした要素の特性をcanvas要素に応用したものです. FireFox/Chrome/InternetExplorerといった広範な環境で動作するので, 通常はこちらを選択すると良いでしょう. 以下に動作原理を示します.
contenteditable=trueを何らかの要素に適用すると, 当該要素において貼り付け操作(右クリックメニュー及びctrl+v)が有効になる. 貼り付け操作を行うと, 配下に子要素が生成される ようになる.
DOM変更のタイミングはMutaionObserverで監視できる.
ペースト操作で追加されたimg要素を抽出し, その内容をcanvas要素に描画する.
例を示します. canvas要素の上に画像のペーストを検知するためのdiv要素を被せ, MutationObserverでDOMの変更を監視しています.
但し, canvas要素に対するコンテキスト(右クリック)メニューが隠されてしまうため, 画像の保存が出来なくなるというデメリットもあります. 何らかの手段で上に被さっているdiv要素を一時的に隠す等の対処が必要でしょう.
canvas要素にcontenteditable属性を設定する方法もありますが, 動作する環境が限られます.
テキストデータの貼り付け
先ほどの例はテキストの貼り付けに応用することができます. この場合, pasteListener内部のテキストを描画するようにします.
クリップボードへの画像転送
逆にcanvas要素の内容をクリップボードに転送する事も出来ます. ctrl+cキーによるコピー操作(コンテキストメニューによるものではない)をcopyイベントで取得し, canvas画像をHTMLコードとしてセットします. コンテキストメニューによる「画像のコピー」がcanvas画像全体をクリップボードに送るのに対して, この方法を用いると画像の一部分を転送可能です. またクリップボードに転送した画像は(HTMLの貼り付けをサポートする)他のアプリケーション(例えばLibreOffice)に直接貼り付けることが出来ます.
下の例では, canvas要素をクリックした後にctrl+cキーを入力することで破線の範囲がクリップボードに転送されます.
とは言え, 貼り付け可能なアプリケーションは限られています.
WEBブラウザでのクリップボードの操作はセキュリティの観点から動作条件に厳しい制約があります.
video要素の描画
canvas要素のソース画像としてビデオ映像を利用可能です. この場合, drawImageメソッドの引数にはHTMLVideoElementオブジェクトを渡します.
描画対象のvideo要素
逆にcanvas要素の内容をvideo要素に出力することも出来ます.
video要素のposter
属性により表示されている画像はcanvas要素に出力されません.
video要素と描画タイミング
video要素の内容を正しくcanvas要素に転写するにはcanplay
イベントの発生を待ちます. video要素による動作再生にはデータの読み込み(load)とは別に, ビデオデータのデコード処理を要するからです.
ビデオフレームの指定
ビデオ映像をcanvas要素に転写すると, その時点でのビデオフレーム映像が出力されます. そのため, HTMLVideoElementのcurrentTime
プロパティと組み合わせて, 任意のビデオフレームを取り出すことが可能です. この場合もcanplay
イベントの発生を待ちます.
WEBカメラの映像の描画
WebRTC をサポートする環境では, WEBカメラの映像をvideo要素を介してcanvas要素に取り込むことが出来ます. navigator.mediaDevices.getUserMedia
メソッドを実行するとWEBカメラ映像がMediaStreamオブジェクトとして得られるため, これをvideo要素で再生しcanvas要素に転写するのです. この機能は例えばWEBカメラによるバーコード /QRコード リーダの実現に応用されます.
WEBカメラ映像を表示するvideo要素
なおgetUserMediaメソッドの所在はかつてnavigator.getUserMedia
とされており, 機能的には同等であるものの使用法が異なります.
Chromeではかつて同様の操作でスクリーンキャプチャ映像を取得することが出来ましたが, 現在ではフラグ設定を含めて機能が削除されています. なお新たにScreen Capture 仕様として内容が整理されていますが, そのまま利用可能となるかは不明です.
getUserMediaメソッド実行の制限
getUserMediaメソッドによるカメラ映像の抽出は使い方によっては重大なセキュリティリスクを伴います. その為, ブラウザではhttps環境でのみ動作可能としているもの(Chrome)があります.
ストリーム画像キャプチャAPIの利用
現在策定が進められているストリーム画像キャプチャ(MediaStream Image Capture)API を利用すると, メディアストリームから直接スナップショットを撮ることが可能になります. 出力結果はファイル(blob)のほか下記に示すImageBitmapオブジェクトとして取得出来るため, これまでどおりcanvas要素を使って内容を加工できます.
Blink系ブラウザで先行実装しており, #enable-experimental-web-platform-features フラグをtrueにすることで動作を確認することが出来ます.
ImageCapture(track)
キャプチャ対象の映像トラックを指定するコンストラクタ関数.
Promise<Blob> imageCapture.takePhoto(photoSettings)
キャプチャ画像をファイルとして取得するプロミスを返す.
Promise<ImageBitmap> imageCapture.grabFrame()
キャプチャ画像をImageBitmapオブジェクトとして取得するプロミスを返す.
下記はcanvas要素から生成したストリーム映像を使ってImageCaptureオブジェクトの動作を確認しています.
ImageBitmapによる画像読み込み
drawImageメソッドに指定する画像としては通常HTMLImageElement/HTMLCanvasElementを用いますが, ImageBitmapオブジェクトをサポートする環境では画像に類するデータ(例えばImageDataやBlob/File等)をImageBitmapオブジェクトに変換することで統一的にdrawImageメソッドに渡すことが可能です. また, DOMから切り離されたオブジェクトであるため, Worker内部でも画像データを扱えるというメリットもあります. なおImageBitmapオブジェクトは種類の異なるコンテキストオブジェクト間での画像データの授受を目的としていて, 今後バックグラウンドでの画像描画やWebGLにおけるテクスチャ画像の取り扱いと言った場面での活用が期待されています.
ImageBitmapオブジェクトの概観
なおWorker環境での動作を想定しているため, DOMに依存したSVG画像をImageBitmapオブジェクトに読み込むことは出来ません.
ImageBitmapFactory(window/worker).createImageBitmap(source, sx, sy, sw, sh, option)
ImageBitmapオブジェクトを生成し, それを利用するためのプロミスオブジェクトを返す. sourceには画像オブジェクトを, sx,sy,sw,shにはその切り出したい範囲を指定する.
sourceとしてはHTMLImageElement, HTMLCanvasElement, HTMLVideoElement, Blob, ImageData, CanvasRenderingContext2D, ImageBitmap, SVGImageElementと言った広範な画像インターフェースを指定可能.
※Window/WorkerオブジェクトはImageBitmapFactoryインターフェースを実装しています.
optionには現状下記値の連想配列(ImageBitmapOptions)を指定可能.
imageOrientation 画像の方向(none/flipY…上下反転)
premultiplyAlpha プレマルチプライ(アルファチャンネルの事前適用の有無)設定の指定(default/premultiply/none)
colorSpaceConversion 色空間の変更(none/default)
resizeWidth リサイズ幅
resizeHeight リサイズ高
resizeQuality リサイズ品質(pixelated/low/medium/high)
colorSpace 色空間
※但し全てが動作するわけではありません.
ImageBitmap WEB環境における汎用ビットマップ(ラスタ)画像を表すオブジェクト. CanvasRenderingContxt2D, WebGLRenderingContext, ImageBitmapRenderingContext共通で利用可能. transferableインターフェースを実装しているため, window-worker/worker-worker間でのデータ授受が可能です. なお, データ転送後は画像データの取り出しが出来なくなります.
createImageBitmapメソッドは直接ImageBitmapオブジェクトを返すわけではなく, それを利用するためのPromiseオブジェクトを返します. つまり, 得られたPromiseオブジェクトのthenメソッドの引数にimageBitmapが得られた際の処理を記述していきます.
ImageBitmap.close()
現在保持している画像データを開放する.
ImageBitmap.width
現在保持している画像の幅
ImageBitmap.height
現在保持している画像の高さ
なおcloseメソッドを指定することで画像データを明示的に破棄することが可能です.
ImageBitmapRenderingContextによる画像データの表示
ImageBitmapが利用可能な環境ではImageBitmapRenderingContextというAPIが追加され, canvas要素に直接ImageBitmapが内包するグラフィックデータを参照させる ことが可能です.
ImageBitmapRenderingContext.transferFromImageBitmap(bmp)
ImageBitmapの内容をcanvas要素に転送する. null
を渡すと画像がクリアされる. また, 渡したImageBitmapの中身は空になります.
※現在transferImageBitmapとの表記揺れがあります
ImageBitmapRenderingContext.canvas
このコンテキストオブジェクトを生成したHTMLCanvasElement
なお, transferFromImageBitmapメソッドはビットマップデータの所有権をcanvas要素に移譲するため, 引数として渡したImageBitmapの中身は空になります.
このようにImageBitmapRenderingContextでは画像の表示を高速に行える一方, 使い途が限られます.
Fetch APIと組み合わせる
Promiseという仕組みを利用するのは一見面倒ですが, 現在検討中のFetch API と組み合わせると次のように画像データの取得処理が順序良く簡潔に記述できます.
CSS画像のcanvas要素への描画
CSS Typed OM Level 1 をサポートする環境では, 背景等のスタイル設定から読み込んだ画像を直接canvas要素に描くことが可能です.
Chrome65以降でサポートしています. これまでは一度image要素で読み込み直す必要がありました.
Element.computedStyleMap() ノードの現在のスタイルをStylePropertyMapReadOnlyオブジェクトとして取得する
StylePropertyMapReadOnly.get(propertyName) プロパティ名に対応するプロパティ値をCSSStyleValueオブジェクトとして取得する. プロパティ値が画像の場合は, CSSURLImageValueオブジェクトが得られる.
CSSURLImageValue.intrinsicWidth 画像の幅
CSSURLImageValue.intrinsicHeight 画像の高さ
CSSURLImageValue.intrinsicRatio 画像のアスペクト比
CSSURLImageValue.url 画像のURL
CSSURLImageValue.state 画像の読込状況(unloaded/loading/loaded/error)
なお, CSSURLImageValueオブジェクトにはloadイベントに相当する仕組みが備わっていないため, 読み込みステータスを元にdrawImageメソッドを実行します.
画像フォーマット別検討事項
以下画像形式特有の検討事項についてまとめます.
JPEG形式…EXIF方向値を加味した画像の描画
JPEG画像にEXIFによる「画像の方向」が設定されている場合, (WebKit以外の)WEBブラウザではこの内容を無視します† . 従ってこの方向を加味した画像の描画を行う場合, 一旦JPEG画像をAjaxによりバイナリデータとして取得し画像の方向データを抽出する必要があります. が, 比較的難度の高い処理となるため, Exif.js と言ったサポートライブラリを用いると良いでしょう.
†img要素を使った場合で, 歴史的経緯から動作を変更出来ないようです. 画像を直接開いた場合と動作が異なる点に注意します. CSS4ではimage-orientationプロパティにfrom-imageを指定することでEXIFによる画像の方向で画像を出力することが可能になります.
EXIF値と画像データの状態
EXIF値 見た目の回転 反転 画像イメージ EXIF値 見た目の回転 反転 画像イメージ
1 なし 実像 ← x,yを交換 (鏡像)
5 左回転 鏡像
2 鏡像 ← x,yを交換 (鏡像)
6 実像 (鏡像の鏡像)
3 180度 実像 ← x,yを交換 (鏡像)
7 右回転 鏡像
4 鏡像 ← x,yを交換 (鏡像)
8 実像 (鏡像の鏡像)
EXIF方向値が得られたら, その内容を元にcanvasのサイズを決定し座標軸を変換します. 下記にサンプルコード(実際の動作例はこちら )を示します.
WebKit系のブラウザではEXIF周りの動作が非常に不安定なため, 必ずメモリ上のImage(HTMLImage)オブジェクトをcanvas要素に転写するようにしてください.
要するにx,y軸の交換 左右反転 180度回転 の有無から定まる\(2\times2\times2=8\)パターンというわけです. この後描画処理を続けるのであれば, 座標軸変換を元に戻すとよいでしょう.
GIF…アニメーションの制御
GIF(APNG)アニメーションファイルをcanvas要素に描画すると, 原則先頭の1フレームが出力されることになっています. が, 一部例外もあります. Safari等のWebKit系ブラウザではdrawImageメソッドの実行を繰り返すことでcanvas要素のグラフィックがアニメーションしてしまいます.
他のブラウザと読み込むフレームを一致させる場合は, loadイベントの直後にGIFアニメーションをcanvas要素に描画するようにします.
アニメーションの擬似制御
canvas要素で生成した静止画像を用いるとimg要素で表示しているGIF(APNG)アニメーションの停止・開始を表現することが可能です. 操作する毎にsrc属性の参照先をGIFファイルとcanvas画像ファイルとに切り替えるのです.
補足)GIF(APNG)アニメーションの挙動
(同一URLの)画像はメモリ内部で共有されています. 従って, 単一ページに同じアニメーションGIF画像を複数箇所に配置した場合, それらは全て同じタイミングで動作します. そのため, スクリプト操作により特定のアニメーションが再読込された場合, アニメーション全体が巻き戻されます. この問題を回避するにはURL文字列の末尾にランダム(シーケンシャル)なハッシュ値(例:a.gif#001
)を追加したものを用います.
クエリ文字列(例:a.gif?001
)を指定した場合はWEBサーバーへのリクエストが発生します.
GIFアニメーションのフレーム分解
GIFアニメーションをフレーム画像に分解するにはjsgif を用います. 下記に例 を示します.
jsgifによるフレーム画像の分割
なお, フレーム分割のみならずアニメーションの再現も可能です.
BMP形式…アルファ値の有無
BMP形式の画像はアルファ値を持つ場合があります. Edgeを除く大抵のWEBブラウザではこのアルファ値を正しく解釈します.
その他の画像形式
JPEG,GIF,PNG,BMP,SVG形式以外の画像形式については, 一旦画像データをImageDataオブジェクトに展開してからcanvas要素に描画します.
(ラスタ形式の)画像データのデコード作業そのものにはcanvas要素は必要ありません. また, デコード結果から直接透過BMP画像ファイルを生成することでcanvas要素を使わずにスクリーンに表示させることも可能です.
ベクタ画像の描画
PDFやWMF等のベクタ形式の画像は専用のライブラリを用いるか, (可能であれば)一旦SVGに変換することでcanvas要素に描画出来ます.
ベクタ画像には描画手続きが記録されているので, この内容を元にcanvas要素にグラフィックを描いていきます.
Service Workerを用いた画像形式変換
Service WorkerはWorkerプロセス(バックグラウンド処理)の一つで, WEBページが要求する様々なリクエストを代行します. この仕組みを用いると, 本来ブラウザがサポートしていない(WEBP, 暗号化した画像データ, SVGやJavaScript等の描画手続き)形式の画像が要求された際に, Service Worker内部で(BMP等の)処理可能な形式に変換して返すことができます. こうすることで, どのような画像リクエスト(img要素, CSS背景, スクリプトからの画像取得等)であっても, 元のHTMLコードに手を入れることなくデコード済みの画像が得られます. また, デコード処理はブラウザ側で実行されるため, ネットワークトラフィックは変化しません.
Service Workerによる画像形式変換
以下にWEBP形式を透過BMP形式に変換するサービススクリプトの例を作りました. Service Workerの動作特性上, WEBサイトをHTTPS環境下で運用する必要がある点と, リクエストを発生しないデータURIスキーム形式の画像については別途対処しなければならないことに注意して下さい.
同様にAPNG(WEBP)アニメーションをXNG(SVG)アニメーションに変換する用途が考えられます.