canvas要素の基本的な使い方まとめ

written by DEFGHI1977

本文書はsvg要素の基本的な使い方まとめの姉妹版として作成を開始しました. canvas仕様そのものの解説のみならず, その使い途についていろいろ言及しています. なお, 筆者の思い込みが混入していたり, まだ使えない機能等満載(そのためスクリプトエラーが発生する場合もあります)だったりと, 内容に間違いがあっても中々検証することができません. またいつの間にか随分なコード量になってしまってえらくページロードに時間がかかります. その部分を了承した上でご利用下さい…

更新履歴

canvas要素の概観

canvas要素とは

canvas要素はHTML5で採用されたWEBブラウザ上で動的にグラフィックを描画するための仕組みです. もともとApple社が自社製OSの機能向上を目的に, 同社製WEBブラウザSafariの独自拡張として策定したものでした. が, その利便性が認識されるとMozilla(FireFox)やOpera等のブラウザベンダーも追随するようになり, HTMLにおける事実上の標準仕様として認識されるようになりました. この流れから, HTML5ではWHATWG/W3Cによって正式に仕様として取り入れられ, 現在Internet Explorerを含むほとんどのブラウザ上でcanvas要素が利用可能です.

canvasの歴史

canvas要素の歴史は意外に古く, Webkit(Safari)では2004年に, Gecko(FireFox)では2005年に導入されています. そこから6年後の2011年にはTrident(Internet Explorer)でも利用可能となり, 現在canvas要素が使えない環境はほぼ無くなりました.

canvas要素に関わる文献は, 発祥からの期間が長いことからWEB上で沢山見つかります. が, 旧世代ブラウザ向けのノウハウや, パフォーマンスの改善・バグの修正, 標準化に伴うAPIセットの名称変更など様々な面で変化が起こっており, (本文書を含め)大抵の場合記事の内容が古くなっています. 参考とする場合はその時期に注意して下さい.

canvas要素の役割

canvas要素にグラフィックを描くには, HTMLDOMで定義されたグラフィック描画APIをJavaScriptから操作します. APIには単純なグラフィックを描くだけでなく, 画像の合成を行ったり画像データへのアクセスを可能とするものなどが過不足無く備わっていて, 画像に関わる広範な処理を行うことが可能です. またcanvas要素に描かれた描かれたグラフィックはimg要素で表示している静的な画像と同等に扱われ, ラスタ画像ファイルとして取り出すこともできます.

正確を期すと, canvas要素はグラフィックの出力を司るノードであり, それ自身は描画機能を持ちません. 描画を行うのは専らCanvasRenderingContextオブジェクトです. こうすることで描画したい内容(2D/3D)に応じて適切なAPIセットを選択可能としています.

canvas要素を取り巻く環境

img要素との違い

その仕組みから「動的なimg要素」としての側面を持つcanvas要素ですが, 機能的に完全な上位互換なわけではありません. 例えばクリッカブルマップを定めるusemap属性はcanvas要素には定義されていません.

canvas要素の今後

現状では単なるビットマップ画像生成機構に見えるcanvas要素ですが,今後はWEB環境における標準的なグラフィック加工インターフェースとしての役割を担います. 例えばWEBカメラで撮った映像をcanvas要素で加工し, それを動画として出力すると言ったことすら可能となります.

canvas要素のすすめ

canvas要素はOSやWEBブラウザの種類に依らず動作することから, 無償で得られる画像編集環境としては最も導入障壁の低いものです. また, スクリプトによる処理結果が見た目に直接反映される点や, 画像処理に必要となる(I/Oやグラフィック)機能セットが一通り揃っていることから, JavaScriptや画像処理の入門に最適と言えます. また, 作成したアプリケーションをWEBサーバー上に配置すればそのままWEBアプリケーションとして公開できるなど, その応用範囲はかなりのものです. 唯一処理の最適化の面で疑問が残るものの, その手軽さ等を鑑みるにcanvas要素をマスターして困ることはありません. まずは試してみて, その懐の広さを是非確かめて下さい.

SVGとcanvas要素の使い分け

しばしばHTML5での競合する技術としてSVGが挙げられますが, canvas要素とは明確な使い分けが可能です. 以下は「グラフィック上での動作に応じて内容を書き換える」ことをSVGとcanvas要素とで表現したものです.

SVGの適用場面

was clicked.

SVGではグラフィックの構造をDOMツリーとして記述することが可能で, 既存のイベントモデルやCSS等の仕組みを活用できます. このことから構造がしっかりとしているユーザーインターフェースやクリッカブルな地図やグラフ等の用途に向きます. また, 印刷を前提としたグラフィックについては出力結果が解像度に依存しないSVGの利用が無難です. 他方見た目が複雑なものや手続き的な処理が必要なものをSVG単体で表現しようとすると途端に実現が難しくなります.

canvas要素の適用場面

canvas要素にはグラフィックに構造を与えるものが少なく, 全ての処理を自分で記述する必要があります. その結果先ほどのSVGの例と比べてコードの記述量が増加しています.

その一方でcanvas要素には画像を操作するための低レベルなAPIが提供されており, 機能的な制約がほとんど存在しません. また, SVGと異なりグラフィック構造を自由に定義することが出来るため柔軟性に富みます. そのためcanvas要素は, 先ほどの例以外の用途(ゲームや画像の生成・加工等)に最適です. また, 関数グラフ等の静的な表現が困難なケースにおいてはcanvas要素による描画が有効な場合があります.

なおその柔軟性故に, canvas要素に対しては個性的なJavaScriptライブラリが多数提供されています. 1から記述すると非常に厄介なcanvas要素ですが, 用途に応じ適切にライブラリを選択することで大幅に作業を省くことが出来るという魅力があります.

canvas要素とSVGの連携

以上まとめるとSVGは「画像描画フレームワーク」を与えるのに対し, canvas要素は「画像描画API」を提供します. このようにSVGとcanvas要素とは抽象化のレベルが異なるため, 組み合わせて利用することが可能です. 例えばcanvas要素で描いた内容をSVGに挿入すると言った工夫をすることで両者の恩恵を一度に授かることが出来ます. 実際SVG2ではHTMLのcanvas要素をそのまま利用可能となり, 今後両者の連携がより親密なものとなっていきます.

canvas要素の利用が可能な環境

canvas要素は現在ほとんどのブラウザ(FireFox, Chrome, Opera, Safari, Edge, Internet Explorer9以降)で利用可能です. とは言えHTML5と同様にcanvas仕様も日々変化しており, 仕様にあるからと言っても全ての機能が使えるわけではありません.

また, ブラウザごとに異なるレンダリングエンジンの特性により細かい描画の差が発生します. そのため, (データレベルでの)完璧な互換動作を求めるのは得策ではありません.

canvas要素のポリフィルライブラリ

また, レガシーIE(6〜8)についても, 擬似的にcanvas要素に対応させるJavaScriptライブラリが提供されています.

なおベンダーによるブラウザサポートはIE11を除き全て終了したため, これらのライブラリもその役割を終えました.

Node.js環境でのcanvas操作

WEBブラウザを介さず直接JavaScriptを実行可能とするNode.jsでは, node-canvasモジュールを導入することでcanvas要素とほぼ同等の機能をもつCanvasオブジェクトが利用可能になります. これによりサーバーサイドでの画像生成やローカルにおけるグラフ作画においてもcanvas APIを用いた処理が可能となります.

canvas要素に対応したスクリプトライブラリ

canvas要素に特化したJavaScriptライブラリには主に次の3種類があります.

複数のライブラリを組み合わせて利用する場合は, その相性を鑑みて動作検証は注意深く行って下さい.

ローカルでの動作検証

ブラウザによっては, ローカル環境上ではcanvas要素の一部の機能を利用することができません. 例えばローカル画像を描画した後にtoDataURLメソッドを呼び出すとセキュリティエラーが発生することがあります. 最も確実な対処策は動作検証要のWEBサーバーを作り, そこに自作のcanvasスクリプトを配置することですが, それが出来ないこともあります. この場合, WEBブラウザの設定を変更することで対応できます.

上記設定を施した環境では多少なりともセキュリティリスクが増加しています. その為HTML文書を開く際はその内容が信頼できるものかについて注意して下さい.

また, APIによっては専用のフラグを有効化しないと動作しないものがあります. これらは仕様・機能の検証中の機能なので, 動作検証の目的以外では利用しないで下さい. うっかりフラグ値を変更していることを忘れていると, 使用不可なAPIに依存したスクリプトを記述してしまうかもしれません.

canvas要素によるラスタ画像の描画

canvas要素を取り巻くオブジェクト群

canvas要素はJavaScriptに対する画像描画APIと考えられます. 以下はcanvas要素を取り巻くオブジェクトの相関を表しており, 黄色いものはcanvas要素仕様に直接関わるものです. グラフィックの入出力まで含めると, 非常に多くのオブジェクトと関わりを持つことが判ります.

canvas要素を取り巻くオブジェクトの相関

canvas要素の基本構造部分

CSSに関わる部分

グラフィック描画を扱う部分

画像入出力に関わる部分

バイナリデータ操作に関わる部分

バックグラウンド動作に関わる部分

ストリーミング出力(WebRTC)に関わる部分

補足)利用可能なAPIについて

なお, 環境によってはここに記載されているAPIの全てが利用可能というわけではありません とりわけInternet Explorerでは(ライフサイクルの観点から)利用可能なオブジェクトやAPIに大きな制約が発生しています. 以下のテーブルはこのブラウザで利用可能なオブジェクトを示しており, ピンク色の機能は利用できないことを表しています.

オブジェクト・APIのサポート状況
CanvasPixelArrayUint8ClampedArrayPath2D
ImageBitmapImageBitmapRenderingContext
atobbtoarequestAnimationFrame
WorkerBlobURL
メソッドのサポート状況(HTMLCanvasElement)
toBlobmsToBlobtransferControlToOffscreen
メソッドのサポート状況(CanvasRenderingContext2D)
drawFocusIfNeededaddHitRegionscrollPathIntoView

グラフィック描画の流れ

canvas要素によるグラフィックの描画は単体では動作せず, JavaScriptから先程示したオブジェクトを操作することで行われます. 以下にその処理の流れについて示します.

  1. canvas要素を記述する(事前準備)
    HTMLソースコードのグラフィックを描きたい箇所にcanvas要素を記述します. img要素と同じようにレイアウトして構いません. なお直接スクリーンにしないのであれば必要ありません.
  2. canvas要素を入手する
    document.createElement("canvas")を用いてカンバス要素を生成するか, 既存のカンバス要素をdocument.getElementById / getElementsByTagName / querySelector / querySelectorAllメソッド等で取得します. 得られるオブジェクトはHTMLCanvasElementオブジェクトです. なお, createElementメソッドによってcanvas要素を生成した場合はその要素をDOMツリーに挿入せず, そのままメモリ上でグラフィックを描く事もできます.
  3. canvas要素のサイズを設定する
    widthプロパティとheightプロパティを使って画像サイズを設定します. canvas要素そのものに属性値が設定されている場合はこの処理は要りません.
  4. コンテキストオブジェクトを取得する
    getContextメソッドを使ってコンテキストオブジェクトを取得します. コンテキストは一般に「文脈」と訳されますが, canvas要素においては絵を描くための「道具」を表します.
  5. コンテキストオブジェクトを介してグラフィックを操作する.

canvas要素を記述する

HTML文書中で動的にグラフィックを描きたい箇所にcanvas要素を記述します. canvas要素に指定可能な属性は以下のとおりです.

width
canvas内部のグラフィック幅を指定する.
height
canvas内部のグラフィック高を指定する.
id,class,style,title,tabindex,dir,contenteditable…
HTMLの要素共通の属性.

canvas要素はレイアウト上img要素と同様に振る舞う(フローコンテンツ)ため, CSSを用いて自由に配置することが出来ます. その際, 後でスクリプトが目的のcanvas要素を検索できるようにid属性やclass属性を定義しておくと良いでしょう. また, width属性やheight属性を記述しておけばスクリプトでのサイズ指定を省くことが出来ます.

canvas要素はフォールバックコンテンツを必要とするので, img要素のように単一タグで記述することは出来ません. (なおXHTMLの場合は問題ありません)

canvas要素はグラフィックを表すので, JavaScriptが無効化されている場合やテキストブラウザ, スクリーンリーダー環境などのためにフォールバック(代替)コンテンツ指定しておきましょう.

スクリプトの記述

スクリプトコードを記述する場所に特に制限はありませんが, DOMを経由してcanvas要素を取得・追加することが多いためDOMの解析が終了したあと, つまりdocumentのDOMContentLoadedもしくはwindowのloadイベントに処理を登録するのが一般的です. 例を示します.

このように全ての画像処理を手続き的に記述することとなるので, グラフィックが複雑になればなるほどコードが長くなります. 従って処理の内容に応じて共通機能をサブファンクション化したり, 外部ライブラリ化するなど処理の流れを明瞭化しておくと, コードの管理がやりやすくなります.

HTMLCanvasElement.width
canvasのグラフィック幅. width属性値に対応する. グラフィック描画後に値を変更すると, グラフィックやスタイル設定が初期化される. width属性未設定の場合300(ピクセル)として扱われる.
HTMLCanvasElement.height
canvasのグラフィック高さ. height属性値に対応する. グラフィック描画後に値を変更すると, グラフィックやスタイル設定が初期化される. height属性未設定の場合150(ピクセル)として扱われる.
HTMLCanvasElement.getContext(type [,arguments])
グラフィック描画を行うためのコンテキストオブジェクトを取得する. 引数の内容によって得られるコンテキストオブジェクトの種類が変化する. なお, 誤ったキーワードを指定した場合, ブラウザが対応する機能を持たない場合はnullが返される.
2d…CanvasRenderingContext2D,
webgl/experimental-webgl…WebGLRenderingContext,
bitmaprenderer…ImageBitmapRenderingContext
コンテキストオブジェクトはcanvas要素につき唯一である.
第2引数にはコンテキストオブジェクトに対するパラメータ(オブジェクト形式)を指定する.
getContextメソッドに渡すパラメータ一覧
種類キー意味
2dalphatrue/falseアルファチャンネルの有効/無効
storagepersistent/discardable(blinkのみ)コンテキストの強制開放の有無→ブラウザ環境の保護
willReadFrequentlytrue/false(geckoのみ)ImageDataの読み書きを多量に行う際の省メモリ化の有無を指定.
※要フラグgfx.canvas.willReadFrequently.enable設定
webglalpha/ depth/ stencil/ antialias/ premultipliedAlpha/ preserveDrawingBuffer/ failIfMajorPerformanceCaveat
※ブラウザが独自のAPIを定義しても良い.
HTMLCanvasElement.probablySupportsContext(type [,arguments])
指定したコンテキストタイプをブラウザがサポートしているかを判定する.

getContextメソッドによるコンテキストオブジェクトの取得

canvas要素内部のグラフィックにアクセスするために, まずgetContextメソッドを実行しコンテキストオブジェクトを取得します. コンテキストオブジェクトには複数種あり, 引数に与えたキーワードによって得られるオブジェクトの中身が変化します. こうすることで用途や目的によって使い分けができるのです. なお, HTMLCanvasElementが実装している機能(例えばtoBlobメソッド)はコンテキストオブジェクトの種類に依らず利用可能です.

この他, ブラウザごとに独自のコンテキストオブジェクトを定義しても良いこととなっています. 従ってgetContextメソッドをオーバーライドすることで, 独自のAPIを備えたコンテキストオブジェクトを自作することもできます. なお, キーワードに対応するコンテキストをサポートしない場合, null値が返されます.

コンテキストオブジェクトからcanvas要素を参照する

コンテキストオブジェクトのcanvasプロパティにはこのコンテキストを生成したcanvas要素が設定されています.

ctx.canvas
このコンテキストを生成したcanvas要素.

レンダリング方式の確定

canvas要素がどのレンダリング方式で描画されるかについてはgetContextメソッドの初回呼び出し時に決定されます. 2度目以降に初回と別のキーワードを指定した場合, nullが返されます.

コンテキストオブジェクトの唯一性

getContextメソッドで得られるコンテキストオブジェクトはcanvas要素につき唯ひとつです. 下のようにコード上は2つのオブジェクトが存在しているように見えても実体は全く同じものとなります.

アルファチャンネルの無効化

canvas要素は当初透明な黒(rgba(0, 0, 0, 0))で塗りつぶされており, そこへアルファチャンネル(不透明度)を含めたグラフィックの合成が行われます. ここでコンテキスト取得時に{alpha: false}を指定すると自動的にアルファ値が1(255)に固定され, ピクセルデータは実体のある黒(rgba(0, 0, 0, 1))で充填されます.

生成する画像に透明部が必要ない場合は, このパラメータを明示することで(僅かながら)描画処理速度の向上が見込めます.

canvas要素における2つのサイズ

canvas要素にはその仕組み上, グラフィックそのもののサイズとグラフィックの見た目のサイズの2つのサイズが定義されます.

canvas要素における2つのサイズ
サイズの記法
 グラフィックそのものサイズグラフィックの見た目のサイズ
対応する記述width/height属性CSSのwidth/heightプロパティ
記述例
未設定時の扱い300×150グラフィックそのものサイズ
備考img要素ではnaturalWidth / naturalHeightとして取得

width/height属性を指定しなかった場合の動作

canvas要素にwidth属性もしくはheight属性が指定されていなかった場合, 往々にして意図した出力結果となりません. 例えば次の例ではCSSで見た目のサイズを200px×200pxに設定しており, そのサイズでグラフィックを青色で塗りつぶそうとしていますが, 実際には300×150のカンバスに描いているため描画結果が狂っています.

グラフィックの見た目のサイズにグラフィックそのもののサイズを合わせる

画像サイズを予め固定出来ない場合, window.getComputedStyleメソッドを利用すると, グラフィックの大きさをcanvas要素の見た目の大きさに合わせることが出来ます.

これを応用すると, canvasグラフィックの大きさに50%と言った比率値を指定することもできます. 但しウインドウのリサイズ時にグラフィックの再描画を行うなどの工夫が必要となるでしょう.

補足)グラフィックのアスペクト比を維持するスタイル

canvas要素に限らずimg要素やvideo要素などの要素をそれを囲むコンテナいっぱいに拡大する際, そのアスペクト比を維持したい場合はobject-fitプロパティにfitを指定します.

補足)canvas要素の最大サイズ

出典:Maximum size of a <canvas> element※2014年当時の調査結果

ブラウザ毎の最大カンバスサイズを下記に示します.

ブラウザ別canvas最大サイズ
最大幅/高最大ピクセル数(消費メモリ)メモリ換算
Chrome32,767268,435,456 (16,384 × 16,384)1GB
Firefox32,767472,907,776 (22,528 × 20,992)2GB
Internet Explorer8,19267,108,864 (8,192 × 8,192)256MB

現在考えられうる最大のスクリーンサイズは8K(7680 × 4320)であるため, 理屈上はどのブラウザにおいてもフルスクリーンでのcanvasグラフィックの描画は可能ということになります. ですが, 動作パフォーマンスを考えると出来る限りカンバスのサイズは小さいほうが良いでしょう.

DOM操作とcanvas要素

canvas要素に描画した内容は, canvas要素そのものの変更を伴うDOM操作の影響を受けます. なお, canvas要素の配置の変更と言った操作の影響は受けません.

widthプロパティ, heightプロパティに対する操作

canvas要素に対するwidth/heightプロパティを操作するとグラフィック全体が初期化されます.

cloneNodeによるcanvas要素の複製

canvas要素はcloneNodeメソッドにより複製できますが, canvas要素に描いた内容までは引き継ぎません.

canvas要素を含む要素のinnerHTMLプロパティを操作する

この場合暗黙的にcanvas要素が再構成されるため, 内容がクリアされます.

出典:innerHTML clears the drawing canvas pixels.

この場合, 代替としてinsertAdjacentHTMLメソッドを用いましょう. 既存のDOM構成が維持されるため, canvasグラフィックに影響を及ぼしません.

環境の確認

現在では少なくなりましたが, canvas要素に対応していないブラウザにおいてはこれまでのコードはエラーとなってしまいます. 従って何らかのトラップコードを実装しておき, 処理の振替を行うようにしておくと良いでしょう. この場合CanvasRenderingContext2Dの実装状況を確認します.

CanvasRenderingContext2Dの拡張

canvas要素を利用するにあたり, よく使う処理は共通ライブラリとして使い回したいものです. その際, CanvasRenderingContext2Dオブジェクトそのものを拡張してしまう方法があります.

事前にCanvasRenderingContext2D.prototypeオブジェクトに関数を定義しておくことで, getContextメソッドで取得したコンテキストオブジェクト全てで利用可能となります. 同様にCanvasRenderingContext2Dがもつメソッドそのものを拡張することも可能です. 下の例ではarcメソッドの角度の指定を省略した場合に円を引くように機能を拡張しています.

図形の描画

canvas要素における座標系

まず図形を描画する上で最も基本となる座標系の考え方について示します. canvas要素における座標は左上が(0, 0)であり, 右下が(width, height)です. つまり通常のCSSにおける座標系と同じです. 単位はピクセルをとります. この範囲から外れた描画は無視されます.

基本的な描画処理の流れ

CanvasRenderingContext2Dオブジェクトを用いたグラフィックの描画の流れは次の通りです.

  1. beginPathメソッドにより描画範囲の開始を宣言する. なお省略すると暗黙的に実行されたこととなる.
  2. moveToメソッドによりパスの起点を指定する.
  3. lineTo, arcTo, quadraticCurveTo, bezierCurveToメソッドによりパス切片を追加していく.
  4. 必要に応じclosePathメソッドを実行してパスを閉じる.
    これらのメソッドを必要な回数繰り返す. これらの図形は次にbeginPathメソッドが呼び出されるまで内部に保持される.
  5. コンテキストオブジェクトにスタイルを設定する.
    これは下記のメソッドにおける色や形の指定, 画像合成の方法を行うものである.
  6. strokeメソッド, fillメソッドを用いてパス図形を描画する.
    現在コンテキストが内部に保持しているパス図形に沿って線の描画, 領域の塗り潰しが行われる. なおこれらの描画処理は画像領域に上書き(合成)していくので, 元の位置に既にグラフィックが存在した場合に描画処理を取り消すことはできない.
図形描画の流れ

この処理を1セットとして描きたい図形の分だけ繰り返していきます. 例を示します.

CanvasRenderingContext2D.beginPath()
パス図形を初期化し, パス図形の開始を宣言する.
CanvasRenderingContext2D.fill([path2d],[fillRule])
現在のパス領域を塗りつぶす. Path2Dをサポートしている場合, 引数に渡したパスデータの領域を塗りつぶす. fillRule…塗り潰し方法の指定 nonzero/evenodd
CanvasRenderingContext2D.stroke([path2d])
現在の領域の境界に線を引く. Path2Dをサポートしている場合, 引数に渡したパスデータの領域を塗りつぶす.

このようにcanvasでの描画処理は逐次上に図形を重ねがけしていく点が特徴であり, 一部の描画の内容を変更する(例えばグラフィックを動かす)と言った場合は処理を原則最初からやり直す必要があります. なおこれは古典的なcanvas要素の使い方であり, 下で紹介するPath2Dオブジェクトを利用すると煩雑なパス図形定義処理をオブジェクトとして管理することができます.

図形の開始宣言

beginPathメソッド実行すると, それまでのパス図形を破棄し新たなパス図形を定義します.

このメソッドを実行せず図形の描画処理を行った場合, 暗黙的に呼び出したものとして扱われるため, どこから新しいパス図形かがわからなくなります.

塗り潰しの順番

strokeによる境界線の上に塗り潰しを施すことでHTMLにおけるborderのような描画を行うこともできます. 先ほどの例のstrokeメソッドとfillメソッドを実行する順番を交換したものを示します.

パスの描画

CanvasRenderingContext2Dオブジェクトには以下に示すパス切片を定義するための6つのメソッドが定義されています.

CanvasRenderingContext2D.moveTo(x,y)
パス切片の起点を指定する. x,y…起点の座標
CanvasRenderingContext2D.lineTo(x,y)
直線を引く.
CanvasRenderingContext2D.arcTo(x1,y1,x,y,radius)
円弧を引く. x1,y1…頂点の座標, x,y…終点の座標, radius…円弧の半径
CanvasRenderingContext2D.quadraticCurveTo(x1,y1,x,y)
2次ベジェ曲線を引く. x1,x2…ベジェ曲線の制御点座標, x,y…終点の座標
CanvasRenderingContext2D.bezierCurveTo(x1,y1,x2,y2,x,y)
3次ベジェ曲線を引く. x1,y1…始点に対する制御点, x2,y2…終点に対する制御点座標, x,y…終点の座標
CanvasRenderingContext2D.closePath()
パス切片を閉じる.
パス図形の定義メソッド群

これらのメソッドを数珠つなぎで実行することで一連なりのパス図形を表現します. 予めmoveToメソッドで起点を指定し, 各メソッドを実行すると引数として与えた終点座標まで線が引かれます. この終点が次のメソッドにおける始点となるのです. なおこれらのメソッドは単にパス図形を定義するだけであり, 線の色や塗り潰しなどのグラフィックの見た目は別のメソッド(stroke, fillメソッド)を用います. つまりパス図形は鉛筆による下書きのようなものです. 事前に色を付ける範囲を決めてから絵筆で塗りつぶすわけです.

パス図形はベクタデータです. 従って, ピクセル画素の境界にまたがる図形を定義することが出来ます. 色を塗る場合はコンテキスト側で見た目が最適になるよう境界部の色味を自動調整します(アンチエイリアス).

起点の指定:moveTo

各種パス切片を定義する際の始点を指定するメソッドです. 直線にしろベジェ曲線にしろ, まずはこのメソッドで始点を指定します.

直線:lineTo

前回の終点から引数に指定した終点まで直線を引きます. 繰り返し実行することで多角形を表現出来ます.

角の丸め:arcTo

2直線に接する円弧を描画します. 引数としては有向線分の端点座標2つと円の半径を与えます. こうすると現在位置-始点(直線1)と始点-終点(直線2)に接する円が求まります. そこで, arcToは現在位置からまず円の接点まで線を引き, 続いてもう片方の接点まで円弧を引き処理を終了します.

このメソッドは角丸四角形を定義する場合に便利です.

2次ベジェ曲線:quadraticCurveTo

制御点と終点を与え2次のベジェ曲線を引きます.

3次ベジェ曲線:bezierCurveTo

始点と終点に対する制御点2つを与え3次のベジェ曲線を引きます.

パスを閉じる:closePath

closePathを実行すると直近のmoveToメソッドで指定した座標まで直線が引かれ, 図形が閉じられます.

逆にclosePathメソッドを指定しなかった場合, 開いた図形となります. fillメソッドはclosePathを実行した際の領域を塗りつぶします.

名称が対照的であるため勘違いしやすいが, beginPathメソッドとclosePathとの間に直接的な関連は存在しません.

矩形と円弧の定義

先程の4つのメソッドでほとんどの図形は定義可能ですが, よく使う矩形(四角形)と円弧の定義については専用のメソッドが提供されています. どちらも, (beginPath〜)moveTo〜closePathまでの一連の操作を一括で指定できるようにしたマクロ的な動作をするため, パス図形の中に組み入れて利用することが出来ます.

CanvasRenderingContext2D.rect(x,y,w,h)
矩形の生成. x,y…矩形の左上頂点の座標, w…矩形の幅, h…矩形の高さ
CanvasRenderingContext2D.arc(cx,cy,radius,startAngle,endAngle,anticlockwise)
円弧の生成. cx,cy…円弧の中心座標, radius…円弧の半径, startAngle,endAngle…円弧の開始/終了ラジアン, anticlickwise…反時計回りフラグ

矩形:rect

rectメソッドを利用すると, 4頂点(つまり8数値)を指定して定義していた四角形を左上の頂点と幅と高さの4つの値で指定することが可能となります. rectメソッドの終点は左上の頂点となるので, 後続のlineToメソッド等はこの頂点を基準に線を描画します.

rectメソッドで引かれるパスは時計回りとなります.

width値やheight値を負の値を指定した場合は反時計回りとなります.

円弧:arc

arcメソッドでは円/円弧を定義します. また扇形を定義することも可能です. 中心の座標と半径, 円弧の開始角と終了角(単位ラジアン)を指定します. 従って真円を描く場合は角度に0〜Math.PI*2を指定し, closePathメソッドでパスを閉じるようにします. 角度の基準は中心から右のラインを0度とし, 時計回りに角度を算出します.

anticlockwiseフラグにtrueを指定すると, 円弧の定義を反時計回りに行います.

扇形を定義する場合は, 一旦円弧の中心にmoveToメソッドで起点を移し, arcメソッド実行後closePathメソッドを実行します.

明示的にbeginPathメソッドが指定されていない場合, arcメソッドは暗黙的にbeginPathを実行し, 始点を円弧の開始点としますが, 他のサブパス生成処理の後にarcメソッドを実行すると円弧の始点まで勝手に直線を定義してしまいます. これは先程の扇形の定義を行うには便利な機能ですが, 場合によっては煩わしいことがあります. このような場合はmoveToメソッドで円弧の開始点にパスの起点を移動させてしまいましょう. 複数の円弧を単一のパス図形として扱う場合に注意して下さい.

このようにarcメソッドに依る図形の定義は制御が難しい面もあるため, 事前にbeginPathメソッドを実行しておき, 他のパス図形と分離した方が見通しが良いでしょう.

楕円弧:ellipse

ellipseメソッドは楕円・楕円弧を定めます. 使い方はarcメソッドと同様ですが, パラメータとしてx軸方向の半径とy軸方向の半径に加え楕円の傾きが追加されています.

CanvasRenderingContext2D.ellipse(cx, cy, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
楕円弧の生成. cx,cy…楕円弧の中心座標, radiusX,radiusY…楕円弧のx軸/y軸方向半径, rotation…楕円弧の回転角, startAngle/endAngle…楕円弧の開始/終了ラジアン, anticlockwise…反時計回りフラグ

パスの中抜きルール

通常, パス図形で囲まれた領域がfillメソッドによる塗りつぶしの対象となります. が, パス図形の定義の仕方によっては, 複数のパスで囲まれた領域が発生します. 従ってこのような領域を塗りつぶしの対象とするかどうかについてのルールを決めておく必要があります. canvas要素(及びSVG)ではこのルールをfillRuleとして定めていて, その基準として領域とパス図形から求まる交差数を用います.

交差数とは, カンバス内の特定の位置から遠くの点(どこでも良い)に線を引き, パスと何回交差したかを表す値です. 但しパスには始点から終点への向きが定まっているので, 向かって左から右に交わった場合に交差数に1を加算し, 逆に右から左に交わった場合に1を減算します. すると, カンバス上の領域は全てこの交差数で分類できるのです.

交差数の算出
交差数の例

fillメソッドはこの交差数を元に塗りつぶす領域を判定します.

nonzero
交差数が0の領域を塗りつぶします. 直感的にはパスの向きが打ち消し合っている部分が中抜きされます. fillメソッド規定の動作です.
evenodd
交差数が奇数の領域を塗りつぶします. 直感的には偶数回重なっている部分は中抜きされます.

nonzeroルールでの中抜き

単一のパス図形のパス切片が重なっていて, そのパス切片の向きが互いに逆向きであった場合, 交差数が0となるため塗り潰しの対象外となります. 例を示します.

evenoddルールでの中抜き

fillメソッドの引数として"evenodd"を指定することで交差数が偶数の領域が中抜きされます.

Path2Dオブジェクトによるパス図形の管理

canvas要素においてパス図形を管理することはこれまで面倒でしたが, 新たに追加されたPath2Dオブジェクトを用いることでこの問題が解決します.

Path2D()
Path2D(path)
Path2D([path], fillRule)
Path2D(pathString)
パス図形を表すオブジェクト. 引数として既存のPath2DオブジェクトPath2Dオブジェクトの配列,fillruleSVGパスデータ文字列の何れかを指定できます.

下の例では, Path2Dオブジェクトを使って図形定義を使いまわしています.

このように図形そのものをオブジェクトとして扱うことが出来るため, 再利用や編集が非常に容易に行えます. 以下はPath2Dオブジェクトを引数に渡すことが可能なメソッドの一覧です.

Path2Dを引数に取るメソッド群
fill(path, fillRule)
stroke(path)
clip(path, fillRule)
isPointInPath(path, x, y, fillRule)
isPointInStroke(path, x, y)
drawFocusIfNeeded(path, element)
scrollPathIntoView(path)
addHitRegion({path: path})

パス図形の逐次定義

Path2DオブジェクトにはCanvasDrawingContext2Dオブジェクトのもつパス図形定義関数「closePath, moveTo, quadraticCurveTo, besierCurveTo, arcTo, rect, arc, ellipse」の全てが定義されています. 従ってコンテキストオブジェクトと同様にパス図形を逐次定義することが出来ます.

このようにコンテキストオブジェクトに対して発行していたメソッドの対象がpathに変更された点とfillメソッドの引数にPath2Dオブジェクトが追加されている他は全く変わりません.

SVGのパス文字列を用いたPath2Dの生成

SVGではパス図形をパスデータ文字列と呼ばれる形式で定義します. Path2Dオブジェクトの生成にはこの文字列を利用することが出来ます. パスデータ文字列を構成するコマンドの意味合いは次の通りです.

SVGのパスコマンド一覧
コマンド内容記述対応するコード
M/m始点に移動するM[x],[y]ctx.moveTo(x,y)
L/l直線を引くL[x],[y]ctx.lineTo(x,y)
H/h水平線を引くH[x]ctx.lineTo(x,[現時点のy座標])
V/v垂直線を引くV[y]ctx.lineTo([現時点のx座標],y)
C/c3次ベジェ曲線を引くC[x1],[y1] [x2],[y2] [x],[y]ctx.bezierCurveTo(x1,y1,x2,y2,x,y)
S/s3次ベジェ曲線を引くS[x2],[y2] [x],[y]ctx.bezierCurveTo([自動計算],x2,y2,x,y)
Q/q2次ベジェ曲線を引くQ[x1],[y1] [x],[y]ctx.quadraticCurveTo(x1,y1,x,y)
T/t2次ベジェ曲線を引くT[x],[y]ctx.quadraticCurveTo([自動計算],x,y)
A/a楕円弧を引くA[rx],[ry] [angle] [largeArcFlag] [sweepFlag] [x],[y]対応する関数なし
Z/zパスを閉じるZctx.closePath()
小文字のコマンドは現在位置からの相対座標として扱います.

先程の例を今度はパスデータ文字列を使って書き直してみましょう.

このように上手く使いこなせれば大幅にパス定義処理の記述を減らすことができます.

パス図形の複製

Path2Dオブジェクトの複製は複製したいパス図形をコンストラクタに渡します.

パス図形の合成

Path2Dオブジェクトは既存のPath2Dオブジェクトのリストを元に生成することも出来ます.

Path2D.addPathメソッドを用いると既存の図形に新たなパス図形を追加することが可能です.

Path2D.addPath(path, transform)
パス図形に他のパス図形を追加します. transformには行列データ(SVGMatrix, DOMMatrix及びそれに類するもの)を渡します.

またaddPathメソッドにはオプションとして変換行列を渡すことが出来ます. 引数に渡したパス図形はこの変換行列で変形されたうえで大本の図形に追加されます.

座標軸変換とPath2D

Path2Dは座標軸から独立した抽象的な図形を表すオブジェクトです. 従って, fill・strokeメソッド等による描画結果はcanvas要素内部の座標軸に応じて変形されます.

補足)棄却された機能群

下記はPath2Dの初期草案に存在したメソッド群で, 現在は仕様から省かれています.

Path2D.addPathByStrokingPath()
パス図形に他のパス図形のストローク図形を追加する.
Path2D.addText()
パス図形にテキスト外形を追加する.
Path2D.addPathByStrokingText()
パス図形にテキスト外形のストローク図形を追加する.

塗りと線のスタイル

スタイル設定

パス図形を定義した後, strokeメソッドで線を, fillメソッドで塗り潰しが行われることはここまで見た通りです. その際に線の太さや色等の様々な設定を行うためのプロパティが提供されています.

CanvasRenderingContext2D.strokeStyle
線のスタイル. 色の他, グラデーション・パターンを指定する.
CanvasRenderingContext2D.fillStyle
塗り潰しのスタイル. 色の他, グラデーション・パターンを指定する.
CanvasRenderingContext2D.lineWidth
線の太さ.
CanvasRenderingContext2D.lineCap
線の端点のスタイル. butt(無し)/round(丸め)/square(四角)
CanvasRenderingContext2D.lineJoin
線の頂点のスタイル. bevel(面取り)/round(丸め)/miter(尖り)
CanvasRenderingContext2D.miterLimit
lineJoinがmiterの場合の尖りの限界比. 初期値は10

グラフィックの色:strokeStyle, fillStyle

strokeStyleは線の色を, fillStyleはパス図形の塗り潰しの色を表す. 色の指定にはHTML色rgb関数/rgba関数#000/#000000 色のHEX(16進)指定hsl関数/hsla関数currentcolorの何れかを指定する(初期値は黒). 不透明度(rgbaにおける4つめの値, alpha. 0で透明, 1で不透明となる. )が指定されている場合は, その内容に応じて色が重ねられる.

fillStyle及びstrokeStyleプロパティは値を参照することも出来ます. 値を参照した場合, HEX値(不透明度が指定されていた場合はrgba形式)での色文字列が返されます.

currentcolorの効能

「currentcolor」をfillStyle/strokeStyleプロパティに指定すると, 値を設定したその時点でのcanvas要素におけるcolorプロパティの色が選択されます.

colorプロパティは祖先要素から継承されるため, canvas要素のプロパティが未指定であっても値をもつ場合があります.

パス図形を固定して異なる描画スタイルを重ねる

単一のパス図形に対してスタイルを変更し, stroke/fillメソッドを続けて実行することで図形を重ねることが出来ます.

同様に, パス図形に関わる処理を連続実行することも出来ます.

線の太さ:lineWidth

lineWidthには線の太さをピクセル単位で指定します. canvas要素では線の中央がパス図形と重なるように線が描かれます.

なお, 線の境界がピクセル境界に合っていない場合や幅が1ピクセルに満たない場合は, 自動的にアンチエイリアスが掛かります. そのため, 出力結果の色が期待していたものと異なると言った現象が起こります.

端点のスタイル:lineCap

lineCapプロパティは端点のスタイルを表します. butt(なし), round(円), square(四角形)の何れかから選択します.

頂点のスタイル:lineJoin

lineJoinプロパティは頂点のスタイルを表します. bevel(なし), round(丸め), miter(鋭角)の何れかから選択します.

miterLimit値の指定

パスの頂点における線の厚み(最大幅)をmiter(留継ぎ)幅と呼び, miter幅とstroke幅との比率をmiter比と呼びます. これらの値は2線分が為す角度\(\theta\)を使って次のように表せます.

\begin{equation} miterRatio = \frac{1}{sin(\theta/2)} = \frac{miterWidth}{lineWidth} \end{equation}
miter比の計算式

図に表すと次のようになります.

strokeWidthとmiterWidthとの関係

lineJoinプロパティがmiterの場合, miterLimitプロパティでmiter比の上限値を設定することが出来ます. つまりmiter幅がmimterLimit×stroke幅値を超えると自動的にbevel(角なし)として扱われます.

破線の描画

strokeで描く線を破線とすることができます. setLineDashメソッドには引数として破線のパターンを配列として渡します. 破線のパターンは[実線(, 間隔, 実線, 間隔...)]として記述します. lineDashOffsetは破線の開始位置をマイナス方向にずらします.

CanvasRenderingContext2D.setLineDash(segments)
破線の設定をする. segments…破線設定の配列
CanvasRenderingContext2D.getLineDash()
破線の設定を配列として取得する.
CanvasRenderingContext2D.lineDashOffset
破線のオフセット値を設定する.

矩形領域に関わる専用メソッド

矩形範囲に対する操作は頻出するため, 専用のメソッドが用意されています. これらの操作はパス図形の定義とは関連せず, 独立して実行することができます.

CanvasRenderingContext2D.fillRect(x,y,width,height)
現在のコンテキストの設定を使って矩形領域を塗りつぶす. x,y…矩形の左上頂点の座標, width…矩形の幅, height…矩形の高さ
CanvasRenderingContext2D.strokeRect(x,y,width,height)
現在のコンテキストの設定を使って矩形の線を描画する. x,y…矩形の左上頂点の座標, width…矩形の幅, height…矩形の高さ
CanvasRenderingContext2D.clearRect(x,y,width,height)
指定した矩形をクリアする. x,y…矩形の左上頂点の座標, width…矩形の幅, height…矩形の高さ

矩形領域の塗り潰し:fillRect

矩形領域の線:strokeRect

矩形領域のクリア:clearRect

canvasは初期の色として全面が「黒の透明(rgba(0,0,0,0))」で初期化されています. clearRectメソッドは指定した矩形範囲の描画の内容をこの黒の透明に初期化します. 下の例では分かりやすくするためcanvas要素のbackground-colorをyellowに指定しています. clearRectメソッドを実行したことで, グラフィックをクリアした部分の背景の色が透けて見えています.

図形描画とアンチエイリアス処理

canvas要素を構成する画素の数は有限であるため, そこに(無限の解像度を持つ)ベクタ図形を描画する上で自ずと近似(アンチエイリアス)処理が発生します. Retinaディスプレイ等の高解像度環境では目立ちにくいものの, 時に厄介な問題を引き起こします.

線の境界のアンチエイリアス処理を回避する

strokeによる線の描画は図形の境界が線の中央となるように描画されますが, 設定によっては美しい結果が得られません. 例えば下の図においてパス図形が黄色の線だとすると, 1px幅のストロークは2ピクセルにまたがってしまうためアンチエイリアスが発生します.

何も対処しなかった場合 何も対処しなかった場合 幅を2の倍数とする場合 幅を2の倍数とする場合 描画位置を0.5ずらす場合 描画位置を0.5ずらす場合
線描画時のアンチエイリアスの発生原理と回避法

このような場合は, lineWidth値を2の倍数としたり, 線の位置を0.5ピクセルずらすといった作業を行うとアンチエイリアス処理を回避できます. とは言えこれはcanvas要素にスタイルが設定されていたり, ディスプレイの解像度によって変化するため制御が難しい面もあります.

図形の一括描画と逐次描画の違い

同様の問題はfillメソッドに依る塗り潰しにおいても発生します. 同じ領域に同じ色を重ねた場合, 論理的には全く同じ結果となるはずですが, パス図形の境界においてアンチエイリアス処理が発生すると, 塗り潰し処理を一括で行うか否かで得られる結果が異なります. 逐次塗り潰しを行った場合, 不透明部が重ね合わされる(アルファマルチプリケーション)ことで, 想定した色よりも濃い部分が発生するのです.

下の例では何度も塗りつぶされている四角形の左上の部分の色が濃くなっています.

描画スタイルをCSSから参照する

描画スタイルの中にはCSSの設定内容を流用可能なものがあります. 例えばフォントスタイルをCSSから取得すると, HTML文書内のテキストとcanvasグラフィック内部のテキストの見た目を統一することができます. canvas要素に現在適用されているスタイルを取得するにはwindow.getComputedStyleメソッドを用います.

window.getComputedStyle(element)
指定した要素の現在のスタイル設定情報を取得する.

以下にcanvasへ流用可能なCSSプロパティのうち, 比較的単純に利用できるものを示します.

canvasへ流用可能なCSSプロパティ
CSSプロパティstyleプロパティ対応するctxプロパティ内容・備考
widthwidth(width)canvasのサイズとする.
正確にはbox-sizingの設定を参照する必要がある.
heightheight(height)
fillfillfillStyle塗りの色†
strokestrokestrokeStyleストロークの色†
stroke-widthstrokeWidthlineWidthストロークの幅†
「px」を取り除く.
stroke-linecapstrokeLinecaplineCapストローク端点の形状†
stroke-linejoinstrokeLinejoinlineJoinストローク頂点の形状†
stroke-miterlimitstrokeMiterlimitmiterLimitストローク頂点の尖りの限界†
stroke-dasharraystrokeDasharraysetLineDash破線のスタイル†
「px」を取り除き, 「,」で分割して利用する.
font-stylefontStylefontフォント設定
内容をスペースで連結する.
font-variantfontVariant
font-weightfontWeight
font-sizefontSize
font-familyfontFamily

グラデーションとパターン

グラデーションの生成

CanvasGradientオブジェクトを用いることで, 塗り潰しや線にグラデーションを設定することが出来ます. CanvasGradientを生成するメソッドとしてはcreateLinearGradientとcreateRadialGradientの2つが提供されています.

CanvasRenderingContext2D.createLinearGradient(x0,y0,x1,y1)
線形グラデーションオブジェクトを生成する. x0,y0…グラデーションの開始座標, x1,y1…グラデーションの終了座標
CanvasRenderingContext2D.createRadialGradient(x0,y0,r0,x1,y1,r1)
放射状グラデーションオブジェクトを生成する. x0,y0,r0…グラデーションの開始円, x1,y1,r1…グラデーションの終了円
CanvasGradient.addColorStop(offset, color)
グラデーションの色の起点を設定する. offset値は0〜1の間で指定する. この範囲外の値を指定した場合エラーとなる.

線形グラデーション:createLinearGradient

createLinearGradientメソッドには引数としてグラデーションの基準となる線分の始点と終点の座標を渡します. 得られたオブジェクトに対してaddColorStopメソッドを実行することでグラデーションの基準となる色(ストップカラー)を設定します. この指定方法はSVGにおけるグラデーションの指定方法に近く, CSS3での角度による指定方法とは使い勝手が異なります.

得られたCanvasGradientオブジェクトは通常の色文字列と同様にfillStyleプロパティ, strokeStyleプロパティに設定することが出来ます.

ストップカラーを設定する順番に特に決まりはありませんが, 同じoffset値が色の境界を表すという特徴からなるべくoffset値が小さいものから記述するようにすると見通しがよいでしょう.

放射状グラデーション:createRadialGradient

createRadialGradientメソッドは引数として2つの円(中心座標と半径)を取ります. colorStop値の設定は線形グラデーションと同様です.

円の中心をずらすことで様々な表情のグラデーションを表現することが可能です. なお, 色の境界がギザギザしていて気になる場合は, offset値をほんの少しずらすことで気になりにくくなります.

グラデーションの繰り返し

SVGやCSSのような繰り返すグラデーションはストップカラーを繰り返し設定することで表現します.

一見面倒ですが, この方法はストップカラーの位置を自由に設定できる点で他のグラデーション定義よりも優れています.

パターンの生成

グラデーションと同じような役割を果たすものとしてpatternがあります. これは図形の塗り潰しや線の描画といったものに任意の図案を指定可能とするものです.

CanvasRenderingContext2D.createPattern(image, repetition)
パターンオブジェクトを生成する. image…パターン画像, repetition…パターン繰り返しの方法.
repeat(両方向(初期値))/repeat-x(水平方向のみ)/repeat-y(垂直方向のみ)/no-repeat(なし)

パターンの生成:creataPattern

元となるパターン画像としてはimg要素の画像canvas要素の画像video要素の映像の何れかを利用します. 例えばパターン画像としてを利用する場合, 次のような結果が得られます.

また動的にcanvas要素を生成し, それをパターンとして利用することも可能です.

自分自身に描いたパターン画像を参照することもできます.

パターンに対する変形

塗り潰しパターンに変形処理を施す為のメソッドが検討されています. なお, 現状でも座標軸の変形処理と組み合わせれば実現可能です.

CanvasPattern.setTransform(matrix)
パターンに変形を施す. matrixには行列情報を格納したdictionary(つまり, DOMMatrixやSVGMatrixでも良い)を渡す.

画像効果・合成

クリップによる描画領域の制限

clipメソッドを実行すると, コンテキストオブジェクトに保持していたパス図形をクリップ領域に変換し, それ以降のカンバスへの描画をこの範囲に制限します.

CanvasRenderingContext2D.clip([path2d],[fillRule])
現在のパス図形の範囲をクリップ領域に変換し, fill, stroke, drawImage, fillRect, strokeRect, clearRectメソッドによるこの範囲外への図形・画像の描画を無効とする. Path2Dオブジェクトをサポートしている場合は引数渡したパス図形オブジェクトでクリップする. fillRule…クリップの方法

clipメソッドは重ねがけすることが出来ます. 重ねる毎にクリップ領域が狭まっていきます.

任意形状の繰り抜き

clip領域はclearRectによるグラフィッククリアにも適用されます.

グラフィックの固定化

パス図形が存在しない時にclipメソッドを実行することでcanvasグラフィックを固定し, 以降ピクセル操作を除く一切の操作を受け付けないように出来ます.

クリップ領域の解除

一度クリップ領域を定義した場合原則元に戻すことはできませんが, 事前にsaveメソッドを実行しておくとrestoreメソッドを実行することでsaveメソッドを実行した時点でのクリップ領域に戻すことが出来ます.

ドロップシャドウ効果

shadowプロパティに値を設定しておくことで, 図形描画時に影を追加することができます. 影は塗り潰し, 線の描画の両方に有効です. その際のスタイルに不透明度が設定されている場合は, 背後に影が描画されているものとして図形がその上に合成されます.

CanvasRenderingContext2D.shadowColor
影の色.
CanvasRenderingContext2D.shadowBlur
影のぼかし幅.
CanvasRenderingContext2D.shadowOffsetX
影のx軸方向のずらす量.
CanvasRenderingContext2D.shadowOffsetY
影のy軸方向のずらす量.

なおshadowColorにグラデーションを指定することはできません.

影のみを描画する

影のみを描画するには, 元となるパス図形をカンバスの範囲外に定義し, offset値を使って影をグラフィック範囲内に引き込むようにします. これは単色の図形に対するぼかし処理を単純化します.

パス図形の内側への影を定義する

出典:Inset shadows with HTML5 Canvas

canvasでの影は通常図形の外側に広がるように定義されますが, CSSでのbox-shadowプロパティではinsetオプションを設定することで図形範囲の内側に広がる影を描くことが出来ます. この動作をcanvasで再現する場合は, 次のように元となる図形を逆方向の矩形で囲み, 図形を反転させてから影をつけるようにします.

不透明度とグローバルアルファ

図形の不透明度を指定するには2つの方法があって, 色の指定をrgba関数で行うglobalAlphaプロパティで透明度を指定するの2つの方法があります.

CanvasRenderingContext2D.globalAlpha
共通不透明度. 描画処理にたいする不透明度を表す. 0で完全に透明. 1で不透明. なお, 透明であっても色の情報は存在している点に注意する.

色の指定にrgba関数を用いた例を示します.

これをglobalAlphaプロパティで書き換えると次のようになります.

globalAlphaの値は何も単色の指定にのみ有効というわけではありません. 例えば外部画像をカンバスに出力する場合の不透明度としても利用可能です.

画像の合成

複数の図形・画像を合成する(重ねる)際, その合成方法をglobalCompositeOperationで指定することが出来ます. この値は既存の色に新たな色を付け加える際の計算式を表し, clipメソッドによるクリップ領域を設定せずとも画像のくり貫き等を実現することができます.

CanvasRenderingContext2D.globalCompositeOperation
画像の合成方法. 下記のcomposite-modeかblend-modeの何れかを指定する. 詳しい処理内容については仕様を参照のこと.
composite-mode(Porter Duff Compositing Operators)
source-atop
A atop B
source-in
A in B
source-out
A out B
source-over
A over B(初期値)
destination-atop
B atop A
destination-in
B in A
destination-out
B out A
destination-over
B over A
lighter
A plus B
copy
A(B is ignored)
xor
A xor B
blend-mode(Compositing and Blending Level 1)
normal
通常(source-overに同じ)
multiply
乗算
screen
スクリーン合成
overlay
オーバーレイ合成
darken
比較暗
lighten
比較明
color-dodge
覆い焼き
color-burn
焼きこみカラー
hard-light
ハードライト
soft-light
ソフトライト
difference
差の絶対値
exclusion
除外
hue
色相
saturation
彩度
color
カラー
luminosity
輝度
 
 

ドロップシャドウ処理は内部的にこの画像合成機能を利用しているため, 設定値を変更してしまうことで影の描画処理に影響を及ぼす点に注意しましょう.

例を示します. 先に赤い円を描画し, その後に青い矩形を描画します. その際の描画結果の違いについて注意して下さい 詳しくはこちらを参照して下さい.

source-atop

元画像に重なった部分のみを描画します.

source-in

元画像をクリアし, 元画像に重なった部分のみを描画します.

source-out

元画像をクリアし, 元画像に重なっていない部分のみを描画します.

source-over

元画像に新たな画像を重ね合わせます. 何も宣言しなかった場合の初期値です.

destination-atop

先ほどのsource-atopと立場を逆転した描画を行います.

destination-in

先ほどのsource-inと立場を逆転した描画を行います. 描画結果の繰り抜きに利用します.

destination-out

先ほどのsource-outと立場を逆転した描画を行います. strokeによる画像のクリア(いわゆる消しゴム)に用います.

destination-over

先ほどのsource-overと立場を逆転した描画を行います. 既存のグラフィックの裏側に図形を置くような効果が得られます.

lighter

重なった部分のrgbの画素毎に明るい(値の大きい)ものを用います. 不透明度による画像の重ね合わせが全体として暗くなるのに対し, ligter合成では逆に明るくなります.

copy

元画像をクリアして図形を描画します. clip機能やcanvas要素の自分自身への書き込みと組み合わせることで様々な応用が効きます.

xor

元画像との共通部分がクリアされます.

darker

webkit専用の合成メソッド. 同等のblend-modeであるdarkenと同等の結果を得ます.

blend-modeの指定

Compositing and Blending Level 1をサポートしている環境ではglobalCompositeOperationに上記のcomposite-modeの他にblend-modeを指定することが出来ます. 一部使い途に乏しいように見えるものも応用次第で様々な処理が行えます.

blend-modeの動作サンプル

copy合成の応用

一見役に立ちそうにないcopy合成ですが, canvas要素の自分自身への書き込みと組み合わせることで既存のグラフィックに対する編集操作が可能です.

画像の平行移動

自分自身の描画位置をずらすことで画像の平行移動を表現できます.

フィルタの書き戻し

描画済みの内容にフィルタを適用する際に利用します.

ピクセルデータの入れ替え

入れ替えたい範囲をclip領域で囲み, その上に描画を行います. アルファブレンディングによる合成処理を行いたくない場合に利用します.

クロスフェード画像の生成

2つの画像を一つに合成する際にglobalAlphaプロパティを使うとクロスフェード画像が得られます.

これはglobalCompositeOperationプロパティを使って書き換えることが出来ます.

この方法を用いると画像の透明度をグラデーション化することが可能で, より高度なクロスフェード画像が得られます.

動きの検知

difference合成は2つの画像の違いを出力します. これは例えばカメラによる動きの検知に応用できます. 時間を空けた2枚の画像を合成した際, canvasに出力される内容が黒(もしくはそれに近い色)一色であればその間何も動かなかった事になります.

色成分への分解と再構成

globalCompositeOperationを用いると画像を成分毎に分解できます. また, 分解した色情報を再度合成することで元の画像を復元することも可能です.

RGB

光の三原色による分解・合成

A

不透明度の抽出.

CMY

色の三原色による分解・合成.

HSL

blend-modeのうち, hue, saturation, luminosity, colorはいずれもHSL色空間に対する操作を表します. 操作毎の色相, 彩度, 輝度値の対応を図にすると次のようになります.

HSL値への分解と合成
この値の対応を元に画像をh,s,l値に分解し, 再度合成し直すことができます.

フィルタ

フィルタとは画像に対して何らかの効果をもたらすもので, 代表的なものとしてはグレイスケール化やセピア化と言ったものが挙げられます. canvas要素が描くグラフィックにおいても様々なフィルタを利用することができます.

WEBブラウザ環境においては様々な方法で画像にフィルタをかけることができますが, 主に「可逆」なものと「不可逆」なものに分けられます. 前者はCSS Filter/SVG Filterとして知られており, 画像そのものには手を入れず, スクリーンに描画する過程でフィルタを適用するものです. 本項で解説するfilterプロパティによるフィルタはcanvas画像そのものを改変するため, 不可逆なフィルタと言えます.

CanvasRenderingContext2D.filter
これから描画する内容に対するフィルタのフィルタ関数を指定する.

コンテキストオブジェクトのfilterプロパティにフィルタ関数を指定すると, 以降のグラフィック描画対象にフィルタ効果が適用されます. これはfilterプロパティの内容が変更されるまで有効となります. なお, canvas要素に描画されている内容に対してフィルタを掛けているわけではありません. なお, 先ほど見たとおりcopy合成と組み合わせることでcanvas要素の描画内容にフィルタを掛けることが可能です.

指定可能なフィルタ関数には次のものがあります. なおフィルタ関数を列挙することで複数のフィルタを重ねがけすることも可能です.

filterプロパティの動作サンプル

フィルタと合成の適用順

filterプロパティとglobalCompositeOperationプロパティの両方が設定されていた場合, まずカンバスに描画しようとしているグラフィックにフィルタが掛けられ, その内容がカンバス上のグラフィックに合成されます. 下ではフィルタと合成の組み合わせを数回繰り返して画像の二値化を実現しています.

SVGフィルタの利用

上記に示したフィルタはいずれも簡易的なものですが, SVGによるフィルタ定義を用いるとより本格的な画像加工が可能です. SVGフィルタを使うにはDOMツリーにおけるfilter要素のidにurl関数を適用したものをfilterプロパティに設定します. 幾つか例を示します.


クロマキー合成に用いたJPEG画像(背景/前景)

このようにcanvas要素単体では実現が困難な画像処理を簡潔に記述できます. とは言え, 余りに複雑な内容の場合グラフィックの描画速度に影響します. フィルタ画像を動的に生成する必要が無いのであれば, フィルタ結果を事前にPNG形式などの静的な画像として保存しておき, drawImageメソッドでcanvasに描画するようにして下さい.

SVGフィルタを用いる際の注意点

テキストの描画

fillTextメソッドとstrokeTextメソッド

canvas要素においてテキストを描画するにはstrokeTextメソッドもしくはfillTextメソッドを用います. フォントの指定はfontプロパティにて行いますが, strokeStyleやfillStyleはこれまでと同様に行えます. また, テキスト描画の最大幅を設定することで文字列が描画範囲からはみ出さないようにすることが可能です. なお描画は一行のみで, 複数行に渡る描画は行えません.

CanvasRenderingContext2D.strokeText(text,x,y[,maxWidth])
文字列のアウトラインを描画する. テキストと描画位置, 最大の幅を指定する.
CanvasRenderingContext2D.fillText(text,x,y[,maxWidth])
文字列を塗りつぶして描画する. テキストと描画位置, 最大の幅を指定する.
CanvasRenderingContext2D.font
フォントの設定. 書式はCSSにおける記述に準ずる. つまり, 下記の値を順に記述する.
font-style
フォントのスタイル. [normal|italic|oblique].
font-variant
フォントの見た目. [normal|small-caps].
font-weight
フォントの太さ. [normal|blod|100〜900].
font-size
フォントの大きさ.
font-family
フォントの種類.

例を示します. 文字列の最大描画幅が指定されていた場合は, それを超過した際に文字の幅が狭めることで指定幅に収めます. 幅広となることはありません. (presto・旧Opera環境では文字の大きさが変化します)

テキストの描画位置の制御

指定した座標に対してどのようにテキストを描画するかは次に掲げるtextAlign, textBaseline, directionの3プロパティで制御します.

CanvasRenderingContext2D.textAlign
文字列の横の描画基準を指定する. start,end値についてはdirectionプロパティがltrかrtlかで変化する.
left
文字列の左端を基準とする.
right
文字列の右端を基準とする.
start
文字列の始点を基準とする.
center
文字列の中央を基準とする.
end
文字列の終点を基準とする.
CanvasRenderingContext2D.textBaseline
文字列の縦の描画基準を指定する.
top
文字列の上端.
hanging
hanging基底線(フォントが持つグリフ整列の基準ラインの一つ)を基準とする. ヒンディー語等.
middle
文字列の垂直位置の中心.
alpabetic
alpabetic基底線を基準とする.
ideographic
ideographic基底線を基準とする. かな漢字など.
bottom
文字列の下端.
CanvasRenderingContext2D.direction
文字列の方向を指定する.
ltr
左から右(left to right) アラビア語等
rtl
右から左(right to left)
inherit
canvas要素及びその祖先から継承(規定値)

横方向におけるテキスト描画の基準設定

textAlignプロパティは基準となる座標に文字列のどこを揃えるかを指定します. 値としては, 文字列の見た目の左端(left)/右端(right), 文字列としての開始位置(start)/終了位置(end)及び中心(center)のいずれかを指定します.

文字列の方向による動作の変化

WEB環境は他言語環境に対応しているため, 言語ごとに異なる文字列の方向についても考慮する必要があります. 先ほどのstart/end値はこの文字列の方向, 左から右(ltr)と右から左(rtl)によって意味合いが変化します. 文字列の方向を指定する方法には次の3つがあります.

unicode-bidiプロパティを使った文字並び方向の強制

なお, direction値を変更しても必ずしも中身の単語の並びが逆となるわけではありません. 一見奇妙に見えますが, 複数言語から構成される文字列を扱うためにこのような設計になっています. 例えばアラビア語では右から左へ文章を記述する一方で数字や英語等を挿入する際に左から右に記述します. その為, 文章の方向とは別に文字毎の並ぶ方向が定められており, このルールに沿って文字毎の描画位置が決定されるのです. 文字の並ぶ方向をもdirection値で制御したい場合は, CSSのunicode-bidiプロパティにbidi-overrideを指定します.

祖先要素のdirection値を継承する

direction値を指定しなかった場合, inherit値であると扱われ, 日本語環境では通常ltrが設定されているものとして扱われます. また, direction値は祖先要素から継承します.

rtlを設定したノード
①②③④⑤⑥⑦⑧⑨⑩
ニッカウヰスキー

文字列の縦位置設定

日本語環境では気付きにくいのですが, 文字の形状を決定するグリフデータには基底線(baseline)と呼ばれる文字揃えの基準となる「高さ」が設定されており, 文字列はこの高さを基準に一文字ずつスクリーンに描画されます. canvas要素ではこの基底線を選択することで縦位置での文字揃えが可能です.

基底線とテキストの描画位置

テキストの描画幅の取得

テキストの描画幅はフォントやグリフの形状によって変化しますが, measureTextメソッドを使うと文字列グラフィックを矩形で囲んだり, 文字列の一部に下線を引くと言った処理を実現することが出来ます.

CanvasRenderingContext2D.measureText()
テキスト幅を算出するTextMetricsオブジェクトを取得する.
TextMetrics.width
テキストの描画幅

TextMetricsオブジェクトの拡張(tobe)

現状ではテキスト幅しか取得できませんがが, 将来的には次のようなプロパティが追加されます.

TextMetrics.actualBoundingBoxLeft
テキストの境界ボックスの左辺.
TextMetrics.actualBoundingBoxRight
テキストの境界ボックスの右辺.
TextMetrics.fontBoundingBoxAscent
フォントの描画上限位置.
TextMetrics.fontBoundingBoxDescent
フォントの描画下限位置.
TextMetrics.actualBoundingBoxAscent
テキストの境界ボックスの上辺.
TextMetrics.actualBoundingBoxDescent
テキストの境界ボックスの下辺.
TextMetrics.emHeightAscent
ベースラインから文字の上端までの長さ.
TextMetrics.emHeightDescent
ベースラインから文字の下端までの長さ.
TextMetrics.hangingBaseline
hanging基底線の位置.
TextMetrics.alphabeticBaseline
alphabetic基底線の位置.
TextMetrics.ideographicBaseline
ideographic基底線の位置.

以下はTextMetricsの中身を列挙してみたものです. 環境によって使えるプロパティが異なります.

WEBフォントをcanvasに描画する

CSSの@font-face規則を利用するとcanvas要素に対しても文字列の描画にWEBフォントを用いることができます. 例えば次のコードをスタイルシートとして宣言しましょう.

こうすることでGoogleが提供しているWEBフォントが利用可能となります. しかしWEBフォントの準備が完了する前にこのフォントを呼び出した場合, 意図した結果が得られません. 例えば次の二つのコードでは描画結果が必ずしもWEBフォントの内容を反映しません. 一般に言われているHTML内部でWEBフォントを呼び出しつつ, window.loadイベントでのスクリプトを実行する方法でも不十分な場合があります.

この問題はWEBフォントの分析がwindowオブジェクトのloadイベント発生したタイミングでも完了しない場合に発生します. 従って単純には描画処理を(setTimeoutメソッド等により)若干遅延させることで発生頻度を抑えることが可能です. このように元来確実に読み込まれるとは限らないWEBフォントを確実にcanvas要素に描画するには, 少々追加のスクリプトを記述する必要があります.

素朴な方法でWEBフォントを確実に描画する

原理的にはWEBフォントの準備が完了したタイミングを検知し, そこを起点として描画処理を開始すれば良いことになります. 従って前項で示したmeasureTextメソッドを利用して処理を自作します.

measureTextで得られるテキスト幅はフォントの種類に依存するため, この値を観察することでWEBフォントのロード状況を確認出来ます. 予め代替フォントを併記したものと代替フォントのみを記述したコンテキストを用意しておき, それぞれで得られる文字列の幅を一定時間毎に比較します. WEBフォントが利用可能となると文字列の幅が変化し比較結果がfalseとして判定されるので, このタイミングを起点として描画処理を開始するのです.

document.fontsのloadingdoneイベントを用いる

CSS Font Loading Module Level 3をサポートしている環境であれば, よりスマートに対処することが可能です. フォントのロード完了時にdocument.fontsオブジェクトがloadingdoneイベントを発生します. 従って, このイベントを起点に文字列を描画すれば良いのです.

図形コンテナとしてのフォント

文字形状(グリフ)を集めたフォントは見方によればベクタ図形の集合とも言えます. 従って, WEBフォントを頻繁に描画する図形のコンテナとしての利用が可能です.

座標軸変換

座標軸の変形と図形の描画

CSSやSVG, canvas要素における通常の座標軸は原点をcanvasの左上にとり, x軸を水平方向に, y軸を垂直方向にとります. 図形はこの座標軸を基準に描かれるため, 座標軸そのものを変換すると描かれる図形もそれに従い変形されます. 代表的な変換を挙げます.

canvas上のアフィン変換

CanvasRenderingContext2Dオブジェクトではこのうちアフィン変換によるものをサポートしており, 図にすると次のようになります.

アフィン変換による図形の変形

ここでアフィン変換は3×3行列として表すことができ, 変換後の座標\((x,y)\)が変換前の座標において\((x',y')\)に相当すると考えた場合, 次のように記述できます.

\begin{matrix} \begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix}& =& \begin{pmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix} \end{matrix} もしくは \begin{cases} x' &=& ax + cy + e\\ y' &=& bx + cy + f \end{cases}
アフィン変換における座標の関係式

CanvasRenderingContext2Dオブジェクトは内部にこの変換行列を保持しており, moveTo・lineTo等のメソッドが呼び出された際, 引数として渡された座標を変換したうえでパス図形を定義します.

以下はこの変換行列を操作するためのメソッドです.

CanvasRenderingContext2D.setTransform(a,b,c,d,e,f)
現在の変換行列を指定した内容で置き換える.
CanvasRenderingContext2D.transform (a,b,c,d,e,f)
現在の変換行列に指定した変換行列を右から掛ける.
CanvasRenderingContext2D.resetTransform()
現在の変換行列を恒等変換(初期値)に戻す.
CanvasRenderingContext2D.scale(sx,sy)
現在の変換行列に指定したスケール変換行列を右から掛ける.
CanvasRenderingContext2D.rotate(angle)
現在の変換行列に指定した回転行列を右から掛ける.
CanvasRenderingContext2D.translate(dx,dy)
現在の変換行列に指定した平行移動行列を右から掛ける.

例を示します. 何れも同じ図形を描画していますが, 座標軸が平行移動されていることから, 結果として得られたグラフィックでは描画位置がずれている事が判ります.

これらのメソッドは重ねがけすることができます.

なお座標軸の変換メソッドの呼び出し順は, 行列の掛け算の順番に相当することから変更することが出来ません.

座標軸変換をまたいだパス図形の定義

座標軸の変換処理は既存の座標定義・パス図形には影響しません. この挙動を用いると, 紙上で定規をずらしていくように座標軸を変換しながら一つのパス図形を定義することが出来ます. これはcanvas要素特有の仕組みで, 面倒な座標計算処理を省くことが出来ます.

座標軸変換の種類

以下に変換メソッドの動作について示します.

行列による変換:transform/setTransform

transformメソッドは現在の変換行列に別の変換行列を(右から)掛け合わせます. setTransformメソッドは現在の変換行列を指定した行列で入れ替えます. translate, scale, rotateは全てこのメソッドに置き換えることが出来ます.

拡大縮小:scale

x軸方向とy軸方向の倍率を指定します. 何れかの値が0の場合は図形の定義が無視されます. setTransformメソッドで書き換えると次のようになります.

回転:rotate

原点を中心とした回転角を指定します. 引数はラジアンで指定します. setTransformメソッドで書き換えると次のようになります.

平行移動:translate

水平方向の移動量と垂直方向の移動量を指定します. setTransformメソッドで書き換えると次のようになります.

変換行列の初期化

現在の変換行列を初期化するにはresetTransformメソッドを実行するかsetTransform(1, 0, 0, 1, 0, 0)として恒等変換行列に入れ替えるようにします.

変換行列オブジェクトの取得

現在の座標変換状況はDOMMatrixオブジェクトを介してgetTransform/setTransformメソッドとして取得・設定が可能です.

CanvasRenderingContext2D.getTrasnform()
現在の変換行列をDOMMatrixオブジェクトとして取得する.
CanvasRenderingContext2D.setTransform(matrix)
現在の変換行列を指定した行列データ(a,b,c,d,e,fプロパティを含むオブジェクト)で上書きする.

しかし, このAPIは定義されてから日が浅いため, 実装しているブラウザはまだ無いようです.

SVGMatrixオブジェクトの利用

その為getTransformメソッドの代替としてSVGMatrixオブジェクトを使う方法を示します. SVGMatrixオブジェクトを使って得た行列の計算結果をsetTransformメソッドでcanvasに適用するのです. 例を示します. scaleメソッドの名称, rotateメソッドの引数の単位に若干の相違があるものの置き換え可能です.

以下に大体のメソッド相関について示します.

座標変換メソッドの対応
座標変換CanvasRenderingContext2DSVGMatrix備考
拡大縮小ctx.scale(sx, sy)m.scaleNonUniform(sx, sy)
m.scale(s)
SVGMatrix.scaleはsx=sy時に置き換え可能.
回転ctx.rotate(rad)m.rotate(deg)canvasではラジアン値, SVGMatrixでは角度値
平行移動ctx.translate(dx, dy)m.translate(dx, dy)
初期化ctx.resetTransform()
ctx.setTransform(1, 0, 0, 1, 0, 0)
m.a = m.d = 1;
m.b = m.c = m.e = m.f = 0;
変換行列ctx.transform(a, b, c, d, e, f)m.multiply([matrix])
行列指定ctx.setTransform(a, b, c, d, e, f)m.a = a; m.b = b; m.c = c;
m.d = d; m.e = e; m.f = f;
逆行列-m.inverse()カーソル位置の算出に利用

補足)廃止となったプロパティ群を使ったgetTransform/setTransformポリフィル

getTransformメソッドが定義される以前では同等の機能をcurrentTransformプロパティが担うこととされており, 環境によってはまだ利用可能です. またFirefoxでは同等の機能をmosCurrentTransformプロパティとして定義しています.

CanvasRenderingContext2D.currentTransform
現在の変換行列をSVGMatrixオブジェクトとして取得・設定する.
CanvasRenderingContext2D.mozCurrentTransform
(firefoxのみ)現在の変換行列を配列で取得する.
CanvasRenderingContext2D.mozCurrentTransformInverse
(firefoxのみ)現在の変換行列の逆行列を配列で取得する.

その為次のポリフィルコードによりgetTransformメソッドの動作を再現可能です. (SVGMatrixは将来的にDOMMatrixのエイリアスとなるため, SVGMatrixを返すようにします.)

座標軸変換を図形の描画に応用する

以下, 座標軸変換の応用について示します.

楕円の描画

円を何れかの方向に引き伸ばすと楕円が得られます. しかし楕円の境界に線を引く際に座標軸の引き伸ばしをしたままstrokeメソッドを実行すると, 線の幅まで引き伸ばされてしまって見た目が良くありません. この問題は一度座標軸を戻すことで解決します. このことからstrokeによる線の描画にも座標軸が用いられていることが判ります.

厚みをもったstroke

上とは逆にstrokeによる線の幅を縦横不均衡とすることで, 線に厚みを持たせることが出来ます.

座標軸を回転させることによる図形の定義

カンバスを逐次回転させ, 点の位置を少しづつずらして行くことで様々な図形を描画することができます. 三角関数の記述が要らないため構造が単純になります.

螺旋

集中線

多角形・星型

タートルグラフィックへの応用

カンバスを回転させたり基準点の位置をずらしていくことで図形を定義する手法を一般にタートルグラフィックと呼びます. タートルグラフィックは本質的に初期位置の指定回転角の指定前進距離の指定の3操作の繰り返し(プログラム)で表現されます. このプログラムによりタートル(カーソル)が動いた軌跡を元に図形を定義するのです.

タートルグラフィックによる図形定義

これらの操作はcanvas要素では座標軸変換として表現できます.

タートルグラフィックコマンドの対応
コマンドcanvasでの操作
起点の指定setpos(x, y)ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.translate(x, y);
ctx.beginPath();
ctx.moveTo(0, 0);
回転角の指定right(rad)ctx.rotate(rad)
前進距離の指定forward(d)ctx.translate(d, 0);
ctx.lineTo(0, 0);

以下はCanvasRenderingContext2DオブジェクトにタートルグラフィックのためのAPIを定義するスクリプトです.

これらを用いると一般のタートルグラフィック描画アルゴリズムをcanvas上に再現できます.

図形の再帰定義とフラクタル図形

参考:タートルグラフィックスで亀と戯れる

タートルグラフィックは再帰処理と相性がよく, 複雑な図形を単純なアルゴリズムで記述できることからフラクタル(自己相似)図形の描画によく用いられます. 下記はタートルグラフィックの描画手法を用いてコッホ曲線を描画しています.

グラデーション・パターンへの適用

パス図形を定義した後, 座標軸変換を施すことで塗り潰しの内容, 線の内容のみに変形を施すことができます.

ズーム・パン機能の導入

座標軸変換を使うとcanvasグラフィックにズーム(拡大・縮小)とパン(平行移動)機能を追加できます. 下の例ではマウスによるドラッグ操作でグラフィックの描画位置を変更可能としています.

グラフィックのレスポンシブ化

静的なラスタ画像は一般に解像度が確定しているため, 描画サイズが変更されるとアンチエイリアシングと言った近似処理が発生してしまいます. しかしcanvas要素では座標軸変換を用いることで, 既存の描画ロジックへの変更を最小限に留めつつ解像度毎に最適なグラフィックを描くことが可能となります. 具体的な手順を示します.

  1. グラフィックの内部サイズを決定する.
  2. canvas要素の見た目のサイズ(CSSサイズ)を取得する.
  3. ctx.scaleメソッドを使ってグラフィックの描画スケールを「見た目サイズ/内部サイズ」する.
  4. ctx.saveを実行し, 現在のスケール設定を保存する.
  5. 以降はこれまでと同様のサイズ200×200のスクリプトを実行する.
  6. canvas要素のサイズを変更しうるイベントに再描画処理を登録する.

次の例では, input[type=range]の入力内容によってcanvas要素のサイズを変更しています. 文字グラフィックが滑らかに拡大/縮小される部分に注目して下さい.

カンバス状態の管理

コンテキスト状態の一時保存と復元

カンバスでの描画処理はコンテキストオブジェクトを何度も使い回して行われます. この時, 図形を描画する都度その色や座標系, クリップ領域を再定義するといったことでは非常に面倒です. このような問題を解決するため, コンテキストオブジェクトには現在の各種設定を一時的に保存するsaveメソッドと元に戻すrestoreメソッドが定義されています. この機能を用いることで一般に難しいクリップ領域の削除や, 座標軸変換状況の復元などの操作が行えます.

CanvasRenderingContext2D.save()
現在のコンテキストの内容をスタックに保存する. 保存されるプロパティ値は次の通り.
座標軸の変形状況, クリップ領域, strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline
CanvasRenderingContext2D.restore()
前回の状態をスタックから取り出す.

この状態の保存機能はスタックとして実装されているため, 一度restoreメソッドを実行してしまうと先ほどsaveメソッドで実行した内容はなくなってしまいます. 現在の状況をまた後で用いたい場合は再度saveメソッドを実行する必要があります. また, 際限なくsaveメソッドを実行した場合スタックが溢れてしまう可能性があるので, saveメソッドとrestoreメソッドとはセットで用いるようにしましょう. 特にtry/catch構文が介在している場合は, 必ずfinally句でrestoreメソッドを呼ぶようにします.

コンテキスト状態の破棄

コンテキスト状態を破棄するAPIは存在しませんが, 次のようにすると現在の画像を維持しつつコンテキスト設定のみをリセットすることが出来ます.

  1. getImageDataメソッドで現在の画像データを取得する.
  2. ctx.beginPath();ctx.closePath();を実行する.
    現在のパス図形をクリアする. (Trident系ブラウザでは下の操作だけではパス図形が残る.)
  3. canvas.width = canva.widthを実行する.
    コンテキスト状態及びグラフィックが破棄される.
  4. (1)で保存しておいた画像データをputImageDataメソッドで書き戻す.

コンテキスト状態の列挙

コンテキスト状態の全体を取得するAPIは存在しないがfor-in構文を使って列挙することができます. 上手く設計すればコンテキストの状態を外部で管理することも可能です. 但しFunctionやObjectと言ったものも含まれてしまうので, 必要となるものを適宜取捨選択する必要があります.

描画状態の一時保存と復元

処理の都合上グラフィックの内容を保存するケースはよくあります. canvas要素に描いた内容をスナップショットとして外部に取り出す方法には幾つかあり, それぞれ用途や特性が異なります. なお, 細かいAPIの説明についてはそれぞれの項目を参照して下さい.

  1. canvas要素によるもの
    最も素朴な方法で, 別のcanvas要素にグラフィックを描画して保持します. 書き戻しはdrawImageメソッドで行います. シンプルで使い勝手が良いものの, 用途に対して消費するリソースが大きすぎる点が気になります.
  2. CanvasPatternオブジェクトによるもの
    現在の描画内容をcreatePatternメソッドでオブジェクト化します. グラフィックの書き戻しにはfillメソッドを用いるため, 同期的に処理出来るメリットがあります. 一方, 画像データの永続化(クッキーやローカルストレージへの格納)には用いることが出来ず, 専ら一時的なデータ保存に利用します.
  3. toDataURLメソッドによるもの
    画像データを文字列として扱うもので, データの永続化が簡単に行えるメリットがあります.
    localStorage.setItem(key, value)
    キーに対する値(文字列)を格納する.
    localStorage.getItem(key)
    キーに対する値(文字列)を取得する.
    localStorage.removeItem(key)
    キーに対する値を削除する.
    一方グラフィックの書き戻しには一旦Imageオブジェクトを介した非同期処理を介する必要があるため, 扱いが面倒です.
  4. toBlobメソッドによるもの
    使い勝手はtoDataURLメソッドと同様ですが, 画像データの保存先としてローカル環境を用いる場合に利用します.
  5. ImageBitmapオブジェクトによるもの
    ImageBitmapオブジェクトはtransfarableインターフェースを実装するため, 異なる環境(window環境/worker環境)でのcanvas要素/OffscreenCanvasオブジェクト間でグラフィックをやり取りすることが可能です. またfetchAPIと組み合わせることで画像の読み込み処理が単純化されます. 一方オブジェクトの生成にはPromiseオブジェクトが介在するため, 非同期処理として記述することになります.
  6. ImageDataオブジェクトによるもの
    グラフィックをバイトデータとして取得するもので, ImageBitmapオブジェクトと同様にデータの授受が容易です.

それぞれの方法の特徴をまとめると次のようになります.

スナップショット取得別の特徴まとめ
場面\方法canvasCanvasPatterntoDataURLtoBlobImageBitmapImageData
SSの保存先(任意)変数変数・localstrage
・ファイル
変数・ファイル変数変数
書き出し同期同期同期非同期非同期同期
書き戻し同期同期非同期非同期同期同期
動作環境windowwindowwindow/?window/?window/workerwindow/worker
備考一時保管に最適localstorage保存用ファイル保存用ファイル読み込みに有効バイナリ編集用

いずれの方法を採用したとしても, 1履歴分画像のスナップショットを撮ると最低でもcanvasグラフィックの分だけ記憶領域(メモリ/ディスク)が消費されていきます. その為, 無用なグラフィック保存はシステムに負担をかける事になります.

描画状態の破棄

カンバスグラフィックの内容をクリアするには主に次の4つの方法が考えられます.

  1. ctx.clearRect(0,0,canvas.width,canvas.height);
    (推奨)コストの低い操作であるため, 特に問題がない限りこの方法を選択して下さい. 最も単純なクリアの方法ですが, 座標軸変換が存在している場合に正しく動作しません.
    この問題はステートのセーブで解決しますが, 肝心の記述量が増えてしまいます.
  2. canvas.width = canvas.width;
    簡単に記述できる反面, グラフィックの他スタイル履歴情報等も全てクリアされてしまいます. また明示的に何を意図しているのか判りにくいのが欠点です.
    ※Trident(Internet Explirer, Edge)ではパス図形のみ維持されるといったようにブラウザ間で動作に違いがあります.
  3. ctx.globalCompositeOperation = "copy"; ctx.rect(0,0,0,0); ctx.fill();
    画像合成を逆手に取った画像のクリア. 座標軸変換等の状態などを気にせずともよく意外に使いやすいです. 但しcanvas全体に演算が走ることがあり, FireFoxではコストの高い操作となります.
  4. ctx.putImageData(ctx.createImageData(canvas.width, canvas.height), 0, 0);
    (非推奨)グラフィックデータのみをクリアできます. origin-cleanフラグがfalseとなっていた場合にエラーが発生します. なお生成したImageDataオブジェクトは使い捨てにせず変数にとっておき, クリアの都度再利用するとよい.でしょう

上記4つの方法についてそれぞれ動作コストが異なります. 特に(4)のputImageDataメソッドによる方法は画像サイズに比例し極端なパフォーマンス劣化が見られます. また(2)のcanvas.widthプロパティによる方法はcanvas要素の内部構造のクリアを伴うため(1)のclearRectメソッドによる方法よりも若干コストが高くなります. 概ね(1)≦(2)<(3)<<<(4)の順で軽快に動作します.

画像の挿入

drawImageによる画像の挿入

カンバスには図形を描画するのみならず, 既存のラスタ(ベクタ)画像ファイルを描画することが可能です.

CanvasRenderingContext2D.drawImage()
画像を描画する. HTMLImageElement/HTMLVideoElement/HTMLCanvasElement等が描画対象. なお, GIFアニメーションや動画については, 特定のフレームがスナップショット的に描画される.

メソッドの引数のパターンには次の3つが存在します.

引数imageにはHTMLImageElement(Imageオブジェクト), HTMLCanvasElement, HTMLVideoElement, ImageBitmapの何れかを指定します. その他の引数の詳しい意味合いは下記の図を参照して下さい.

画像描画時のマッピング

描画可能な画像のフォーマット

基本的にブラウザがサポートしている(img要素で表示可能な)画像・動画形式であればcanvas要素に描画できますが, ブラウザ互換性を鑑みると, PNG, JPEG, GIF形式のいずれかを利用するのが無難です. なお, JPEG画像についてはCMYK色空間で保存されたものについては扱いに注意が必要です. 環境によっては色味が変化したり, そもそも表示できない事もあります. またWEB環境では基本的に色をRGB値(24bitカラー)で扱うため, canvas要素単体での(カラープロファイルを利用するような)色味の調整は出来ません. 同様にデジタルカメラで撮影したrawデータをcanvas要素に描画する事は出来ず, 別途画像データを24bitカラーに変換する処理を自作する必要があります.

画像挿入の3パターン

下はimg要素で読み込んだ画像ファイルです. これをカンバス要素に描画してみましょう.

座標指定

指定した座標を起点に画像が描画されます. canvasサイズからはみ出た部分は無視されます.

矩形指定

矩形に従って画像が引き伸ばされます.

トリミング指定

画像を指定した矩形範囲で切り取り, その内容をカンバスに描画します.

画像挿入に関わるテクニック

画像の挿入に際して覚えておくと便利なテクニックについて示します.

座標軸変換による画像の変形

drawImageメソッドでは予め座標軸を変形しておくことで画像を変形することができます. 下の例は画像を左右反転させた例です.

canvas要素の参照

drawImageメソッドでは画像の参照先としてcanvas要素が使えます. 例えばアニメーション処理を行う際に前景と背景とを別々に描画し, 最後に一つにまとめるといった構成を採ることも可能です.

また自分自身を書き込むことも出来ます.

画像の大きさを取得する

img要素における画像サイズにおいてもcanvas要素と同じく画像そのものサイズと画像の描画サイズの2つが定義されています.

HTMLImageElement.width/height
画像の見た目のサイズを取得する. CSSによるサイズが指定されていない場合, スクリーンに描画されていない場合は画像そのものサイズを返す.
HTMLImageElement.naturalWidth/naturalHeight
画像そのもののサイズを取得する.

例えば画像の加工を目的としているのであればnaturalWidth/naturalHeightを用います.

画像を読み込む場合の注意点

外部画像を読み込む場合, HTML文書に定義されているimg要素を参照する動的にHTMLImageElementを生成して参照するImageBitmapを生成して参照する(詳しくは後述します)の3つの方法が存在しますが, この時注意すべき点があります. 外部の画像ファイルの読込・分析はメイン処理とは別に非同期で行われるため, drawImageメソッドを実行したタイミングによってはまだ画像のロードが終わっていないことがあります.

画像の読み込みと画像の取得

従ってこの状態のままdrawImageメソッドを実行してしまうと画像の書き込みに失敗します. 下の例ではimg要素に画像の参照先を設定した直後にdrawImageメソッドを実行しているため, 画像の描画成否が不定となります(ブラウザキャッシュを参照した場合や, ブラウザの種類によっては成功することがあります).

この問題を解決するにはimg要素の持つonloadイベントでカンバスの描画処理を行うようにするか, img要素のcompleteプロパティを使って画像読み込みの完了を確認するようにします. 画像の読込が確実に完了してからdrawImageメソッドを実行するわけです.

複数の画像ソースを利用する場合

canvas要素に読み込む画像が複数にわたる場合はもう少し細工を施す必要があります. 読み込まれた画像の数を数え全ての画像が読み込まれたことを確認してから描画処理を開始するようにします. HTMLImageElement.completeプロパティは画像を読み込んでいない際にもtrueを返すためこの用途においては適切ではありません.

画像の描画品質

画像をカンバスに描画する際の品質を設定できます. 主にドット絵等の低解像度の画像データを拡大した際に発生するアンチエイリアスの有無・品質を指定するために用います.

CanvasRenderingContext2D.imageSmoothingEnabled
画像を描画する際の品質を指定する. falseでアンチエイリアスを無効とする. (図形描画等には影響しない)
CanvasRenderingContext2D.imageSmoothingQuality
アンチエイリアスの品質を指定する. (low/medium/high)

画像縮小時の品質改善

出典:Html5 canvas drawImage: how to apply antialiasing

drawImageメソッドで大きな画像を極端に縮小描画した際, 得られた結果において色の境界のギザギザが目立つ場合は縮小作業を何回かに分けることで見た目が改善します.

画像のモザイク化

ソース画像をモザイク化するには様々な方法がありますが, imageSmoothingEnabledが使えるなら一旦小さなcanvas要素に描画した内容を引き伸ばすだけで実現できます. この方法ならピクセル操作を行う場合に注意すべきクロスオリジンでの制約を考えずに済みます.

補足)CSSを用いた画像のモザイク拡大

なお, CSSを使って縮小した画像を拡大する方法もあります. canvas要素側の処理が単純になる反面, 指定するスタイル値がブラウザ毎にバラバラという難点があります. (これはimage-renderingプロパティの内容が二転三転しているため.) またInternet Explorer9以降では-ms-interpolation-modeプロパティが効きません.

出典:Showing how to zoom up a bitmap with crisp edges using HTML Canvas or CSS.

画像読み込みの応用

ここまでは単純なURLを用いた画像読み込みを行いましたが, これを応用すると次のような場面においても画像を取得可能です.

Ajax機構で取得したバイナリ画像データをcanvas要素に書き出す

XMLHttpRequestオブジェクトを使い, 画像データをバイナリデータ(Blobオブジェクト)として取得する方法です. 得られたBlobオブジェクトはURL.createObjectURLメソッドでBlobデータスキームに変換することで, img要素に渡すことが可能です.

一旦画像ファイルをBlobとして扱う方法は, GIFアニメーションをフレーム画像に分割すると言った場合に用いられます.

ローカル環境の画像ファイルをcanvas要素に書き出す

input[type=file]要素で画像ファイルを取得する方法です. 得られたFileオブジェクトはBlobオブジェクトでもあるため, 画像ファイルが得られた後は先ほどと同じです.

canvas要素にドロップした画像を描画する

dropイベントから画像ファイルオブジェクトを取得する方法です. 画像ファイルが得られた後は先ほどと同じです.

いずれも何らかの手段で画像データに相当するBlob(File)を得るURL.createObjectURLメソッドを用いてBlobをimg要素に表示するimg要素には予めonloadイベントにcanvas要素への描画処理を記述しておく描画処理が完了したらURL.revokeObjectURLメソッドでBlobオブジェクトを開放すると言った手順をとっている点に着目しましょう.

クリップボードを経由した画像の描画

出典:How can I let user paste image data from the clipboard into a canvas element in Firefox in pure Javascript?

WEBページ上の画像を右クリックした際に表示される「画像のコピー」を使ってコピーした画像を, ペースト操作(キーボード操作を含む)でcanvas要素に画像を描画する事が出来ます. 実現方法には概ねpasteイベントを用いるcontenteditable属性を用いるの2つがあります.

コピー用の画像(右クリックでコピーして以下のコードでペーストしてみましょう)

pasteイベントを用いるもの

pasteイベントを使って, クリップボードの中身を参照する方法です. canvas要素そのものはpasteイベントを発生させないので, canvas要素を囲む何らかのHTML要素を用意する必要があります. また, そのままではペースト可能な範囲が不明瞭なので何らかの誘導が必要となるでしょう. 動作する環境はChromeに限られます.

MutationObserverによるもの

contenteditable属性をtrueとした要素の特性をcanvas要素に応用したものです. FireFox/Chrome/InternetExplorerといった広範な環境で動作するので, 通常はこちらを選択すると良いでしょう. 以下に動作原理を示します.

例を示します. canvas要素の上に画像のペーストを検知するためのdiv要素を被せ, MutationObserverでDOMの変更を監視しています.

canvas要素にcontenteditable属性を設定する方法もありますが, 動作する環境が限られます.

テキストデータの貼り付け

先ほどの例はテキストの貼り付けに応用することができます. この場合, pasteListener内部のテキストを描画するようにします.

クリップボードへの画像転送

逆にcanvas要素の内容をクリップボードに転送する事も出来ます. ctrl+cキーによるコピー操作(コンテキストメニューによるものではない)をcopyイベントで取得し, canvas画像をHTMLコードとしてセットします. コンテキストメニューによる「画像のコピー」がcanvas画像全体をクリップボードに送るのに対して, この方法を用いると画像の一部分を転送可能です. またクリップボードに転送した画像は(HTMLの貼り付けをサポートする)他のアプリケーション(例えばLibreOffice)に直接貼り付けることが出来ます.

下の例では, canvas要素をクリックした後にctrl+cキーを入力することで破線の範囲がクリップボードに転送されます.

video要素をcanvas要素に描画する

drawImageメソッドの引数にはHTMLVideoElementオブジェクトを渡すこともできます.

描画対象のvideo要素

getUserMediaを用いてWebカメラの映像をcanvas要素に描画する

WebRTCをサポートする環境では, WEBカメラの映像をcanvas要素に取り込むことが出来ます. navigator.mediaDevices.getUserMediaメソッドを実行するとWEBカメラ映像がMediaStreamオブジェクトとして得られます. このMediaStreamオブジェクトをvideo要素で再生し, canvas要素に転写するのです.

WEBカメラ映像を表示するvideo要素

なおgetUserMediaメソッドの所在はかつてnavigator.getUserMediaとされており, 機能的には同等であるものの使用法が異なります.

出典:HTML5 での映像と音声の取得

getUserMediaメソッド実行の制限

getUserMediaメソッドによるカメラ映像の抽出は使い方によっては重大なセキュリティリスクを伴います. その為, ブラウザではhttps環境でのみ動作可能としているもの(Chrome)があります.

ImageBitmapによる画像読み込み

drawImageメソッドに指定する画像としては通常HTMLImageElement/HTMLCanvasElementを用いますが, ImageBitmapオブジェクトをサポートする環境では画像に類するデータ(例えばImageDataやBlob/File等)をImageBitmapオブジェクトに変換することで統一的にdrawImageメソッドに渡すことが可能です. また, DOMから切り離されたオブジェクトであるため, Worker内部でも画像データを扱えるというメリットもあります. なおImageBitmapオブジェクトは種類の異なるコンテキストオブジェクト間での画像データの授受を目的としていて, 今後バックグラウンドでの画像描画やWebGLにおけるテクスチャ画像の取り扱いと言った場面での活用が期待されています.

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)
※但し全てが動作するわけではありません.
ImageBitmap
WEB環境における汎用ビットマップ(ラスタ)画像を表すオブジェクト. CanvasRenderingContxt2D, WebGLRenderingContext, ImageBitmapRenderingContext共通で利用可能. transferableインターフェースを実装しているため, window-worker/worker-worker間でのデータ授受が可能です. なお, データ転送後は画像データの取り出しが出来なくなります.

createImageBitmapメソッドは直接ImageBitmapオブジェクトを返すわけではなく, それを利用するためのPromiseオブジェクトを返します. つまり, 得られたPromiseオブジェクトのthenメソッドの引数にimageBitmapが得られた際の処理を記述していきます.

promise.then(onfulfilled, onrejected)
処理成功時の処理, 失敗時の処理を指定する.
promise.catch(onrejected)
処理失敗時の処理を指定する.
Promise.all([promise, …])
PromiseオブジェクトをひとくくりとしたPromiseオブジェクトを返す.

なおcloseメソッドを指定することで画像データを明示的に破棄することが可能です.

ImageBitmap.close()
現在保持している画像データを開放する.
ImageBitmap.width
現在保持している画像の幅
ImageBitmap.height
現在保持している画像の高さ

ImageBitmapRenderingContextによる画像データの表示

ImageBitmapが利用可能な環境では通常利用するコンテキストオブジェクトに加え, ImageBitmapRenderingContextというAPIが定義されます. 本オブジェクトはグラフィック描画に伴うメモリ書き換え処理の代わりにImageBitmapが内包するデータを直接参照することで画像をスクリーンに出力します.

ImageBitmapRenderingContext.transferFromImageBitmap(bmp)
ImageBitmapの内容をcanvas要素に出力(転送)する. nullを渡すと画像がクリアされる.
※現在transferImageBitmapとの表記揺れがあります
ImageBitmapRenderingContext.canvas
このコンテキストオブジェクトを生成したHTMLCanvasElement

fetchAPIと組み合わせる

Promiseという仕組みを利用するのは一見面倒ですが, 現在検討中のfetchAPIと組み合わせると次のように画像データの取得処理が順序良く簡潔に記述できます.

ImageBitmapによる処理効率化の条件

ImageBitmap/ImageBitmapRenderingContextオブジェクトの仕組みを活かすには幾つか条件があります.

EXIF方向値を加味した画像の描画

JPEG画像にEXIFによる「画像の方向」が設定されている場合, WEBブラウザではこの内容を無視します. 従ってこの方向を加味した画像の描画を行う場合, 一旦JPEG画像をAjaxによりバイナリデータとして取得し画像の方向データを抽出する必要があります. が, 比較的難度の高い処理となるため, Exif.jsと言ったサポートライブラリを用いると良いでしょう.

EXIF値と画像データの状態
EXIF値見た目の回転反転画像イメージ EXIF値見た目の回転反転画像イメージ
1なし実像
x,yを交換
(鏡像)
5左回転鏡像
2鏡像
x,yを交換
(鏡像)
6実像
(鏡像の鏡像)
3180度実像
x,yを交換
(鏡像)
7右回転鏡像
4鏡像
x,yを交換
(鏡像)
8実像
(鏡像の鏡像)

EXIF方向値が得られたら, その内容を元にcanvasのサイズを決定し座標軸を変換します. 下記にサンプルコード(実際の動作例はこちら)を示します.

画像の出力

データURIスキーム形式での画像出力

canvas要素に描画した内容はtoDataURLメソッドを用いることでデータURIスキーム形式の文字列で抽出することができます. これはcanvas要素で描いたグラフィックを再利用する上で重要な役割を果たします.

HTMLCanvasElement.toDataURL(type[, param])
データスURIキーム形式で画像を取得する. ブラウザがサポートしない形式が渡された場合, PNG形式が選択される.
image/png
PNG形式で取得する.
image/jpeg
JPEG形式で取得する. ※第2引数にJPEG品質値(0〜1)を設定可能.
image/webp
WEBP形式で取得する. Chrome等で有効. ※第2引数にWEBP品質値(0〜1)を設定可能.

以下の例ではカンバスに描いた内容をtoDataURLで文字列に変換しています. toDataURLメソッドの引数には, 画像の形式をMIME-typeとして指定します. 通常は「image/png」もしくは「image/jpeg」の2種類が利用可能ですが, 環境によってはこれ以外の形式を指定することも可能です.

image/png…PNG形式

PNG形式は画像品質が維持されるため, グラフや図等の出力, 及びグラフィックを後々に再加工する際の一時保管に向きます. その一方でデータサイズが大きくなりがちです.

生成した文字列:

image/jpeg…JPEG形式

JPEG形式は非可逆圧縮を施すことで大幅に画像データを縮小します. パラメータとして圧縮度(0:高圧縮/低品質〜1:低圧縮/高品質の範囲)を指定できます. なお圧縮の過程でで画像品質が失わることから劣化が目立ち難い自然画や最終的な画像出力に向きます. また, 不透明度を表すアルファチャンネルに対応していないため, 背景色が黒色として画像が生成されます.

生成した文字列:

image/webp…WEBP形式

Chrome等のBlink系のブラウザでは, WEBP形式での出力をサポートします. WEBP形式をサポートしない環境ではPNG形式として出力されます.

生成した文字列:

この他, 環境によってはBMP形式での出力が可能です. 以下にこのブラウザがサポートしている出力形式について示します.

本ブラウザがサポートしている画像出力形式

データURIスキーム形式の画像データ

この「data:image/xxx;base64,」から始まる文字列がデータURIスキーム形式で表現された画像ファイルであり, 後続の文字の羅列はbase64形式に変換された画像のバイナリデータです. RFC 2397「The "data" URL scheme」で定められています.

この形式はテキストファイルの中に画像を埋め込む用途で用いられ, img要素のsrc属性やCSSのbackground-imageプロパティにそのまま記述することが出来ます. またa要素のhref属性に設定してユーザーに画像ファイルを保存させることも可能です.

リンクのサンプル
画像埋め込みのサンプル

HTMLコードに画像データを埋め込んだ例

toBlobメソッドによる画像ファイルの取得

BlobはHTML5から導入されたオブジェクトで, バッファデータとコンテンツタイプをひとまとめにしたものです. Fileオブジェクトの基底オブジェクトでもあり, メモリ内部に展開されたファイルのようなものです. HTMLCanvasElementではtoBlobメソッドを用いることでcanvas要素に描画した内容を直接画像ファイル化することが可能です.

HTMLCanvasElement.toBlob(callback, type, encoderOptions)
画像データに対応するBlobオブジェクトを生成し, 処理を行う. callbackにはBlob生成後の処理を, type,encoderOptionsにはtoDataURLメソッドと同じく画像形式とそのパラメータを指定する.
HTMLCanvasElement.msToBlob()
Internet Explorer専用. 画像データに対応するBlobオブジェクトを同期的に直接生成する. 得られる画像データはimage/png形式固定. 上記toBlobとは使い方が異なるため注意が必要.

BlobをURL文字列に変換する

Blobそのものを編集することはできないものの, URL.createObjectURLメソッドを用いることでリンクやimg要素のsrc属性に指定可能なURL文字列に変換可能できます.

URL.createObjectURL(blob)
Blobオブジェクトを直接参照するURL(「blob:」から始まるBlobURIスキーム)を生成する.
URL.revokeObjectURL(bloburl)
URL.createObjectURLメソッドで生成したBlobURLを廃棄し, Blobオブジェクトへの参照を開放する.

Blobを用いた画像データの送信

toBlobメソッドで得られたBlobをそのままFormDataに設定して送信することが出来ます. 例を示します.

参考)Blobを直接生成した例

toBlobメソッドをサポートしていない環境では次のようにしてBlobを生成することが可能です.

Blob(array, options)
Blobオブジェクトを生成するコンストラクタ. arrayにBlob化したいデータを配列として指定する. optionにはBlob化する際のパラメータを指定する. ここではtype値で画像形式を指定する.
  1. canvasのtoDataURLメソッドを実行し, データURIスキーム形式のPNGデータを取得する.
  2. 上で得られた文字列からヘッダーとなる「data:image/png;base64,」を除去する.
  3. 上で得られたデータ部文字列をatobメソッドによりバイトデータ文字列に変換する.
  4. Uint8Arrayオブジェクトを生成する. 長さは上出えたバイトデータ文字列長とする.
  5. Blobコンストラクタを呼び出し, 「image/png」形式のBlobオブジェクトを生成する.

以下は上記をまとめて独自にtoBlobメソッドを実装したものです. (msToBlobメソッドによる出力はPNG形式に固定されてしまうため, 無視してよいでしょう.)

補足)外部ライブラリによるPNG形式の最適化

canvas要素によるPNG画像の出力では通常24bitカラー+8bitアルファチャンネルが用いられます. これは必ずしもグラフィック内容に対して最適化されたものとはなりません. 出力する画像データを最適化し, よりコンパクト化する必要がある場合は次のJavaScriptライブラリを用いるとよいでしょう.

なお開発者のブロク解説による通りそのまま利用するには非常に重いライブラリであるため, 後述するWeb Workersと組み合わせて利用すると良いでしょう. なおPngEncoderコンストラクタに渡すパラメータについてはソースコード等を参照してください. 例を示します.

GIFアニメーションの動作を制御する

GIF(APNG)アニメーションファイルをメモリ内部でcanvas要素に描画すると, 最初の1フレームが出力されます. この動作を応用してimg要素で表示しているGIF(APNG)アニメーションの停止・開始を表現することが可能です. 操作する毎にsrc属性の参照先をgifファイルとcanvas画像ファイルとに切り替えるのです.

ピクセルの操作

ピクセル情報の取得

canvas要素の内容はImageDataオブジェクトを用いることでピクセル単位で操作出来ます. これまでのメソッドは仕様の解釈の違いから描画結果においてブラウザごとに微妙な差異が発生することがありますが, ImageDataオブジェクトを介した操作はピクセルの色を直接操作することとなるので, 原理的には全く同じ描画結果を得られます. また, 画像データをバイト配列として取得できることから, 様々な画像処理アルゴリズムやグラフィックライブラリをブラウザ上で再現することが可能です.

ImageDataオブジェクトに関わるメソッドとしては次の3つが提供されています.

CanvasRenderingContext2D.createImageData(width,height)
指定したサイズのImageDataオブジェクトを生成する. もしくは元のImageDataと同じサイズのImageDataオブジェクトを生成する. なお, 直接生成することも出来る.
CanvasRenderingContext2D.getImageData(x,y,width,height)
canvas要素の指定した矩形領域に相当するImageDataを取得する.
CanvasRenderingContext2D.putImageData(imagedata, x, y)
ImageDataの内容をcanvas要素に書き戻す.
ImageData.width
ImageDataの幅.
ImageData.height
ImageDataの高さ.
ImageData.data
ImageDataが内部に保持しているピクセルデータ. Uint8ClampedArrayオブジェクト. ImageDataの左上ピクセルから右上→一段下の左上ピクセル→右上ピクセル…右下ピクセルの順に並んでいる. 各ピクセルデータはrgbaの4つの値から構成されており, 全体としての長さはwidth×height×4となる. 配列の中身は0〜255の値で構成されている.
Uint8ClampedArray.length/CanvasPixelArray.length
ピクセルデータ配列のサイズを取得する.
Uint8ClampedArray.slice(start, end)
配列のサブ配列を取得する. 配列の中身は元の配列と共有される.
Uint8ClampedArray.set(array, offset)
配列の内容をarrayで上書きする.offsetは上書きを開始する位置を指定する.
Uint8ClampedArray.buffer
配列の内部構成(メモリ)を取得するプロパティ.

図で表すと次のようになります.

ピクセル情報の構成

ImageDataオブジェクトはcanvas要素におけるピクセルの配列に相当するオブジェクトであり, ctx.createImageData(width, height)(中身は黒の透明/ゼロクリアで充填されている)ctx.createImageData(imageData)(サイズのみ引き継ぐ)ctx.getImageData(x, y, width, height)(canvas要素の描画内容のスナップショットを格納する)の何れかを実行することで取得することが出来ます.

imageDataのdataプロパティに実際のピクセル情報を格納するデータ配列(Uint8ClampedArray)が設定されています. このdataプロパティの中身を操作することでピクセル毎の操作が可能となります. dataプロパティのデータの並びは図のように左上から右下までを1列に並べたものとなっており, 1ピクセルはR(赤)G(緑)B(青)A(不透明度)から構成されています. 例えば座標\((2,3)\)(左上ピクセルを\((0,0)\)とする場合)の緑の値を取得する場合はimageData.data[(3 * width + 2) + 1]とします.

ImageDataのdataプロパティを編集する場合は0〜255の間で値を編集します. これはcanvas要素における画素データのrgba要素が256階調(8bit)で表現されていることによります. なお0〜255の範囲外の値を設定した場合はUint8ClampedArrayオブジェクトの仕様により強制的に0もしくは255に切り詰められます.

ImageDataの編集が完了したら, putImageDataメソッドでその内容をcanvas要素に書き戻します. ImageDataの左上の座標をのみを指定する方法と, ImageDataにおける矩形範囲を指定して書き戻す方法があります.

下記はfillRectメソッドを使用せず矩形範囲を自力で塗りつぶした例です.

なおputImageDataメソッドによる画像の配置は, 現在のクリップ領域及び座標軸の変形による影響を受けず, canvasの画素を直接描き換えます.

putImageDataに矩形範囲を指定した場合, ImageDataのその矩形範囲外のピクセルデータはcanvas要素に書き戻されません.

型付き配列の特性を活かす

dataプロパティの実体Uint8ClampedArrayは型付き配列(ArrayBufferView)であり, bufferプロパティの内容を別の型付き配列に渡すことが出来ます. 得られた型付き配列は元のdataプロパティと中身を共有しているため, 型付き配列に対する操作は直接dataプロパティに反映されます. この仕組みを利用すると様々な処理が単純化されます.

切り詰め処理を省く

色成分に対する演算結果が0〜255の範囲に収まることが判っている場合, Uint8ClampedArrayによる切り詰め処理は無駄と言えます. この場合, ピクセルデータをUint8Array形式に変換することで僅かながら処理の高速化が狙えます.

ピクセルデータをまとめて取り扱う

ピクセル毎の色を成分毎に分解する必要のない場合や配列操作の回数を減らしたい場合, ピクセルデータを格納しているUint8ClampedArrayを一旦Uint32Arrayに変換すると操作が単純化され, 処理速度が向上します.

Uint32Arrayを使ったピクセルデータの集約

RGBA値の復元

この方法で得られたピクセルデータからRGBA値を復元するにはちょっとした細工が必要です. Uint32Arrayに変換した過程でWEBブラウザが動作する環境のエンディアン(複数バイト値を整数に換算する際の並び順)に依存した値となるからです. 下記にその相関を示します.

エンディアン毎の整数解釈
4バイト並び
(RGBA)
32bitIntとしての値
big endianlittle endian
1a2b3c4d
0x1a2b3c4d(RGBA) 0x4d3c2b1a(ABGR)

この関係を念頭に置きつつ, ビット演算を施すことでRGBA値を抽出・上書きします.

行データをまとめて取り扱う

画像をピクセル毎に扱うのではなく, 行ごとに一括して扱うことも出来ます. slice/subarrayメソッドで得られた行データはsetメソッドで一括で書き込むことが出来ます.

色のRGBA値を得る

canvas要素でグラフィックを描く場合, 色情報の形式を統一したい場合があります. 例えばHTML色「navy」に塗りつぶされているピクセルを抽出したい場合, まずnavyに対応するRGB値を求めねばなりません. このような場合は実際にその色を描画してみて, ピクセルの値を抽出すると良いでしょう. fillStyleプロパティを参照しても良いのですが, 文字列として返されるため使い勝手が良くありません.

rgba:(hex:)

フィルタの実装

filterプロパティによるフィルタ(SVGフィルタを含む)だけでは表現できないものでも, ピクセル操作で実現可能なものであれば自作できます.

RGB交換

ピクセル毎のRGB(A)値を交換し, canvasに書き戻します.

グレイスケール

ピクセル毎にR,G,Bの重み付き平均を取ることでグレイスケール化することが出来ます.

減色

ここでは例として単純なものを示します.

この他にもぼかしや, 輪郭抽出などのコンボリューション行列フィルタの実装も可能です. 詳しくはこちらを参照して下さい. なお, 画素毎に大量の計算処理を行うこととなるため, 画像サイズが大きくなるにつれブラウザの動作パフォーマンスに影響を及ぼします. 従って下記に示すasm.jsを使った高速化やworkerを使った処理のバックグラウンド化と組み合わせることを検討しましょう.

位置の変位による画像の変形

ピクセル位置を変異させることで画像を変形することが可能です.

横方向のスライド変形

ピクセル位置を横にずらすことで波のような効果を得ます.

極座標変換

先程の例を発展させ, ピクセル位置の算出に極座標変換を適用したものです. 描画ピクセル位置\((x, y)\)と中心からの半径\(r\)・偏角\(\theta\)との関係からから元画像のピクセルデータを取得しています.

\begin{cases} r = \sqrt{x^2 + y^2}\\ \theta = atan2(y, x) \end{cases} \begin{cases} x = r \times cos(\theta)& (r \leq 0)\\ y = r \times sin(\theta)& (0 \leq \theta < 2\pi) \end{cases}
直行座標\((x, y)\)と極座標\((r, \theta)\)との関係
極座標変換の適用結果画像

ピクセル補間処理による品質の改善

変換の過程で算出された座標に対応するピクセルデータを取得する際, 先程の例では位置的に最も近いものを採用していました. この方法はニアレストネイバー法と呼ばれ高速に処理できる反面, 変換した画像の出力品質がよくありません. この場合, 算出された座標を囲むピクセル群から何らかの補間処理を施したものを用いることで品質が改善します. ここでは最もシンプルなバイリニア法による補間処理を適用した例を示します. バイリニア法は得られた座標を囲む4ピクセルの値から重み付け平均をとるもので, シンプルながら良好な出力結果が得られます.

バイリニア法の概念図
バイリニア補間を適用した出力結果

補足)ピクセル操作にasm.jsを適用する

一般にピクセル操作は型付き配列に対する数値演算の塊と言えます. 従って, asm.jsを適用することで処理速度の向上を見込めます. 簡単な例を示します.

ImageDataの直接生成とメモリの再利用

ImageDataオブジェクトはcreateImageDataメソッドを用いずとも直接生成可能です. ImageDataオブジェクトを何度も使い捨てにする用途であれば, 予め多めのメモリを確保しておくことで単一のメモリ領域を使いまわすことが出来ます.

ImageDataが利用するメモリの再利用
  1. 十分なサイズのメモリ領域(ArrayBuffer)を確保する.
  2. Uint8ClampedArray(ArrayBufferView)を生成する.
  3. ImageDataを生成する.
  4. ImageData(もしくはUint8ClampldArray)の内容を編集しcanvasに書き戻す.

この方法を使えばAjax機構で得た画像の生データ(ArrayBuffer)を直接canvasに読み込ませることも可能です.

オフスクリーンレンダリング

オフスクリーンレンダリングとは

一般にスクリーンに表示されているcanvas要素に直接描画を行うことはコストが高い操作です. なぜなら, apiを呼び出す毎にスクリーンが描き変わってしまうからです. この特性は単一のグラフィックのサイズが小さいうち, 書き換え頻度が少ないうちはそれほど問題となりませんが, アニメーションや複雑な描画処理を必要とする場合に深刻なパフォーマンス劣化をもたらすことがあります. そこで一旦グラフィックをスクリーン描画処理の影響を受けない場所で完成させた上で, スクリーンに書き戻す方法が考えられます. これをオフスクリーンレンダリングと呼びます.

canvas要素を用いる上でオフスクリーンレンダリングを実現する場合, 別途HTMLCanvasElementを直接用いる方法と, 専用のカンバスオブジェクトを用いる方法の2つが挙げられます. なお, 後者は現在APIが策定中であり一部の環境でのみ動作可能です.

基本のオフスクリーンレンダリング

canvas要素におけるオフスクリーンレンダリングを行うには次のようにします.

  1. スクリーン表示用のcanvas要素(A)を取得する
  2. グラフィック描画用のcanvas要素(B)を生成する
    これはスクリプト変数に格納するだけでdomに追加する必要はない.
  3. (B)にグラフィックを描画していく
  4. (B)が描きあがったらその内容を(A)に転写する
  5. ※繰り返す場合は3〜4を繰り返す.

OffscreenCanvasの利用

このオフスクリーンレンダリングに特化したAPIとしてOffscreenCanvasオブジェクトが提案されています. グラフィックの描画処理をOffscreenCanvasオブジェクトで行い, その結果をcanvas要素に書き戻すようにするのです. そのため, HTMLCanvasElementオブジェクトにはtransferControlToOffscreenメソッドが追加されています.

HTMLCanvasElement.transferControlToOffscreen()
OffscreenCanvasオブジェクトを生成する.
OffscreenCanvas(w, h)
直接OffscreenCanvasオブジェクトを生成する.
OffscreenCanvas.getContext(type [, arguments])
グラフィック描画のためのコンテキストオブジェクトを取得する.
内容はHTMLCanvasElement.getContextに同じ.
OffscreenCanvas.width
カンバス画像の幅
OffscreenCanvas.height
カンバス画像の高さ
OffscreenCanvas.toBlob(type, option)
画像データを処理するためのPromiseオブジェクトを取得する.
OffscreenCanvas.transferToImageBitmap()
画像データをImageBitmapオブジェクトとして取得する.
CanvasRenderingContext2D.commit()/WebGLRenderingContext.commit()
オフスクリーンカンバスでの描画結果を元のカンバスに書き戻す.

OffscreenCanvasオブジェクトでの描画結果を複数のcanvas要素に描画する場合は, ImageBitmapオブジェクトを介してグラフィックを転写します.

Web Workersを用いたバックグラウンド処理化

以上はブラウザのメインスレッドを用いたグラフィックの描画でしたが, これをより効果的に行うためにはWorkerオブジェクトを使ってグラフィック描画処理を別スレッドで行わせます.

Workerオブジェクトを生成するとメインとなるブラウザ処理とは別のプロセスが起動し, 非同期に処理をさせることが可能となります. なおWorker内部では利用可能なオブジェクトに制限があるため, 現状データ分析や他言語からの画像処理アルゴリズムの移植と言ったケースに利用されます.

ブラウザ(Worker呼び出し)側

一般的なオブジェクトと同様にWorkerインスタンスを生成し, postMessageメソッドで処理を引き渡します.

Worker(url)
Workerを生成する. 引数としてバックグラウンド処理化したいスクリプトのURLを指定する.
Worker.onmessage = function(e){}
バックグラウンド処理からのメッセージを受信した際のコールバック関数を指定する. addEventListenerメソッドを使っても良い.
引数eはMessageEvent. dataプロパティにWorker内部で呼び出されたpostMessageの引数が設定されている.
Worker.onerror = function(e){}
バックグラウンド処理においてエラーが発生した際のコールバック関数を指定する. addEventListenerメソッドを使っても良い.
引数eはErrorEvent(message/filename/lineno)
Worker.postMessage(message[,[transfer]])
バックグラウンド処理にメッセージを送信する. 引数には処理に渡したいデータをオブジェクト(連想配列)にまとめて指定することが可能. これはつまり, どのようなメッセージをWorkerに送るべきかについての設計が必要ということでもある.
ポストしたメッセージデータは通常バックグラウンド処理に渡す際にコピーされるが, ArrayBufferオブジェクト(Transferableなオブジェクト)であれば第2引数にリストとして記述しておくとバックグラウンド処理に転送(アクセス権が移譲)されデータコピーに伴う負荷を回避できる. なお, 転送されたオブジェクトは元のスレッドからはアクセスできなくなる.
Worker.terminate()
バックグラウンド処理を強制終了する.

Worker内部

変数selfはグローバルスコープを参照するので一般にwindowと同じものを指しますが, Worker内部ではWorkerGlobalScopeを指します. そのため, 特定のオブジェクト(基本データ型, 配列, Uint8Array等のTypedArray, setTimeout/setInterval, XMLHttpRequest等)以外は利用できません.

WorkerGlobalScope.postMessage(data[,[transfer]])
ブラウザ処理側にメッセージを送信する. 引数には処理結果として返したいデータをオブジェクトにまとめて指定する. なお第2引数に指定したArrayBufferオブジェクト(Transferableなオブジェクト)はメイン処理に転送される.
WorkerGlobalScope.onmessage = function(e){}
ブラウザ処理側からのメッセージを受信した際のコールバック関数を指定する. addEventListenerメソッドを使っても良い.
引数eはMessageEvent. dataプロパティにブラウザ側で呼び出されたpostMessageの引数が設定されている.
WorkerGlobalScope.close()
バックグラウンド処理を終了する. これ以上Workerによる処理が必要なければ, 明示的に呼び出してプロセスを開放する必要がある.
WorkerGlobalScope.importScripts(url[, …])
バックグラウンド処理内で使いたいJavaScriptライブラリを読み込む.

図にすると次のようになります. スクリーン描画を行うメインスレッドとWorkerスレッドとはmessageイベントを介して非同期に通信を行うため, 一般的なメソッド呼び出しのように同期的に処理結果が得られるわけではありません.

Workerを使ったオフスクリーンレンダリング

Web Workersを利用した例を示します. データの授受にgetImageDataメソッドで取得したUint8ClampedArrayを利用しますが, 内部のArrayBufferがTransferableなのでこれをpostMessageメソッドの第2引数に指定しています.

SharedArrayBufferによるピクセルデータの共有

Workerとの間で画像データを処理の都度転送し合うのは無駄な処理と言えます. そこで時期ECMA ScriptではSharedArrayBufferオブジェクトが提案されており, この仕組みを用いるとメインスレッドやWorkerスレッド間でメモリを共有することが出来ます.

OffscreenCanvasを使って描画処理をWorkerに委譲する

OffscreenCanvasオブジェクトはTransferableインターフェースを実装しています. そのため, transferControlToOffscreenで生成したOffscreenCanvasをWorkerコンテキスト側に転送してcanvas要素の描画を行わせることが可能です.

canvas要素におけるセキュリティ

origin-cleanフラグによるセキュリティ制御

canvas要素はグラフィックを描画する上で様々な機能を提供しており非常に高機能である反面, 様々な危険性を孕んでいます. そのため, 下記に示す一部のメソッドについては特定の条件下においては実行できないように制限されています.

これらのメソッドの利用可否はcanvas要素の内部でフラグ値として管理されており, このフラグをorigin-cleanフラグと呼びます. canvas要素が生成された直後はこのフラグはtrueに設定されていますが, 下記のような同一生成元ポリシーを侵すような操作が為されるとフラグの値がfalseに設定されます.

ここで「同一生成元(same-origin)」であるとは参照しているurlのプロトコルが同一ポート番号が同一ホストが同一であることを指します. 例えばWEBページhttp://www.sample.com/main.htmlと生成元が同一と判定されるものは, 次の5つのうち上の2つのみです.

フラグの値がfalseの場合に上記メソッドを実行するとセキュリティエラー(SecurityError)が発生します. またフラグの値は連鎖するため, origin-cleanフラグがfalseのcanvas要素の内容を別のcanvas要素に描き込んだ場合, 描き込み先のcanvas要素のフラグもfalseとなります. また, 一旦falseとなったorigin-cleanフラグはAPI等から操作することはできないので, 継続してcanvas要素のフル機能を使いたいのであれば新たなHTMLCanvasElementを生成しなければなりません.

origin-cleanフラグの連鎖

例を示します.

次はorigin-cleanフラグが連鎖する例です.

この「異なるドメイン」の判定が, ブラウザによって異なります. 特に上位ディレクトリを参照する「../」を利用した画像の参照を行った場合, 同一のドメインに存在する画像であるにもかかわらずorigin-cleanフラグがfalseと判定されることがあります. 例を示します.

corsによる動作の緩和

canvas要素におけるoregin-cleanの動作はより一般的なクロスドメインでのリソース参照についての仕様を規定するcors(Cross-Origin Resource Sharing)に準拠しています. ブラウザ, WEBサーバー共にcorsに対応している必要はありますが, WEBサーバーがレスポンスヘッダーにAccess-Control-Allow-Origin値を適切に付加することにより, origin-cleanフラグを汚染すること無くクロスドメインでの画像参照が可能となります.

HTMLImageElement.crossorigin
corsによるリクエストの方法を指定する. レスポンスヘッダーにAccess-Control-Allow-Origin値が設定されていた場合, 元の文書と生成元が同一であるものとして扱われる.
anonymous
認証無しのリクエストを行う.
use-credentials
認証ありのリクエストを行う.
[未指定]
これまでどおりのリクエストを行う.

canvas要素における画像リソースの隠蔽

WEBを介したデータのやりとりは必然的にデータの複製を伴います. 従って, 金銭的に価値のある画像をユーザー側のブラウザに表示しつつ画像の複製を禁止することは事実上不可能です. しかし要件によってはこの矛盾した内容を強引に解決すべき事もあり得ます. ここでは簡単ではありますがcanvas要素を用いてユーザーによる画像コピーを防ぐ(困難にする)方法(つまり, アプリケーションベンダーサイドのセキュリティ)について考察します.

アニメーションの実現と利用

基本的なアニメーションのパターン

canvas要素そのものにはアニメーションを行う機構は存在しません. 従ってJavaScriptの機能を使ってアニメーションを実現します.

基本的な処理の流れは次の通りです.

  1. 事前に描画したい画像等をロードしておく.
  2. canvas要素の内容の一部/全体をクリアする. (全体上書きが発生するなら必要なし)
  3. 図形の位置を算出する.
  4. 図形を描画する.
  5. この2〜4の処理を繰り返す.
  6. 規定回数繰り返した, もしくは規定時間が過ぎたら処理を終了する.

この繰り返し処理を実現する方法には無限ループを用いる(非推奨)setIntervalメソッドを用いる(ゲーム等)setTimeoutを用いる(古典的)requestAnimationFrameを用いる(汎用)等様々なものがありますが, 最も素朴な方法はsetTimeoutによる処理でしょう.

アニメーション処理は実装の方法によって動作パフォーマンスが大きく変化します. 繰り返し処理の中でよく使われるオブジェクト, 配列, グローバル関数, 文字列リテラルなどについてはできるだけローカル変数として持たせるようにするとよいでしょう.

アニメーションタイミング制御APIの利用

アニメーションを実装するのであれば, 古典的なsetTimeout関数によるループ処理よりも専用のアニメーションタイミング制御APIを用いましょう. これはスクリーンの再描画に合わせて処理を呼び出す他, フォーカスを持たないタブや背後で動作しているウインドウでの呼び出し頻度を低くすることで, アニメーションコストを最適化する効果をもたらします.

window.requestAnimationFrame(callback)
アニメーションフレームを要求する. return handle値. callback関数の引数にページ表示時からの経過時間(高精度)が渡される.
window.cancelAnimationFrame(handle)
アニメーションの実行をキャンセルする.

サンプルを示します. requestAnimationFrameメソッドを用いる場合, 単位時間あたりの描画回数が不定となるため, アニメーションの進捗を描画回数でなく, 現在時刻から開始時刻を引いた値で考えます.

高精度タイムスタンプを利用しアニメーションの精度を高める

performance.nowをサポートする環境ではDate.nowメソッドの代わりに用いることでアニメーションの精度を上げることが出来ます. Date.nowメソッドが現在時刻の整数ミリ秒表現(DOMTimeStamp)を返すのに対し, (window.)performance.nowメソッドは当該ドキュメントの表示開始時からの経過時間を浮動小数点数ミリ秒形式(DOMHighResTimeStamp)で表現したものを返します. ミリ秒以下の精密な現在時刻が得られるため, それに伴いアニメーションの精度が高まるのです. なお, Date.nowメソッドとは時刻値の基準がそれぞれ異なるため, 混在させることは出来ません.

Web Animations APIをcanvas要素に応用する

WEB環境におけるアニメーションの実現にはCSSアニメーションによるもの(主にSVGにおける)SMILアニメーションによるものスクリプトによるものの3つがあり, これらは並行して発展してきました. しかし本質的に同じものであることから, アニメーション機構を改めてWeb Animationsという単一の概念・仕様の下でそれぞれを再定義する試みが為されています. スクリプト向けには新たにWeb Animations APIが定義され, アニメーションを“作る”のではなく“制御”する統一的な手段が提供されます.

Web Animations APIでは基本的に特定のノードに対するスタイルを徐々に変化させることでアニメーションを表現しますが, その過程でアニメーションの進捗値が得られます. canvas要素ではこの値を元にグラフィックを描くことでアニメーションを実現します. 手順を示します.

  1. KeyframeEffectオブジェクトを生成する.
    Animationオブジェクトを生成するために, まずどのようなアニメーションとするか(アニメーションの長さ, 繰り返し回数など)をKeyframeEffectオブジェクトで定義する.
  2. Animationオブジェクトを生成する.
    コンストラクタには上記KeyframeEffectオブジェクトのほか, document.timelineを渡す. これはアニメーションをこのdocumentの管理下に置くことを示す.
  3. Animationのステータス値に応じたcanvas描画処理を制御する
    アニメーションには"idol"(停止中), "pending"(実行待ち), "running"(実行中), "paused"(一時停止中), "finished"(完了済み)の状態が定義されており, これらの内容を元にcanvasグラフィックの描画を継続する(つまりrequestAnimationFrameメソッドを実行する)かどうかを判断する.
  4. Animationの進捗値を元にcanvas描画処理を記述する
    アニメーションからはアニメーション進捗値(progress:0〜1)が得られる. この値は(1)で記述したアニメーション設定を反映しており, この内容を元にグラフィックを描くことでAnimationオブジェクトを使ったアニメーションの制御が可能となる.
  5. playメソッドを実行し, アニメーションを開始する.
    一時停止, キャンセルと言った処理をAnimationオブジェクトに任せる.

描画結果を活かしたアニメーション

通常canvas要素を用いたアニメーションでは一旦グラフィック全体を削除して一から描画し直します. が, アニメーションの内容によってはそのようなことをせずに, 描画済みの内容を次のフレーム画像に活かす方法もあります.

アニメーション処理の実際

単一のグラフィック生成ではそれほど気にならないものの, canvas要素によるアニメーションはその実装の仕方でパフォーマンスに雲泥の差が生まれます. 以下に注意すべき点について考察します.

オフスクリーンレンダリングを活用する

画面に表示されているcanvas要素に対する操作はスクリーンの書き換えを伴います. 一般にこの書き換えはコストの高い処理であるためできるだけその頻度を減らしたいところです. そこで一旦メモリ内の(スクリプト変数に格納された)HTMLCanvasElementインスタンスに対して1フレーム分描画した後, DOM内のcanvas要素に複写する(オフスクリーンレンダリング/ダブルバッファリング)ようにします. 画面には常に出来上がったフレームのみが描画されるため, 必要最低限のスクリーン書き換えで済みます. またこれを応用すると, 内部的に1秒間に60フレーム分の描画を行い, 実際の表示にrequestAnimationFrameメソッドを用いることも可能です. この場合, スクリーンの描画頻度を変更することが容易となります.

オフスクリーンレンダリングを用いたアニメーションの構造

グラフィックを構造化(レイヤ化)する

頻繁に書き換える必要のない背景画像については別のcanvas要素に描画するようにし, 複数のcanvas要素を重ねることで目的のグラフィックを得るようにします. つまり無理に単独のcanvas要素に収めるのではなく, 画像合成をWEBブラウザ側に任せるのです. 更にその際の処理コストを考慮して前面に配置するcanvas要素は必要最低限のサイズとしておき, CSSで描画位置を指定するとより効果的です. 同様にbackground-imageを使ってcanvasグラフィックをレイヤ化する方法もあります.

canvasグラフィックの階層化

するとスナップショット画像を撮れないように思えますが, 保存アクションの際にcanvasを合成すれば良いだけの話です. また, canvas要素が重なることでイベントの取得が面倒な場合は, pointer-eventsプロパティをnoneとすることで上位レイヤを構成するcanvas要素に対する操作が無効になります.

stroke/fillメソッドの実行回数を減らす

JavaScriptによるcanvasAPIの呼び出しにはコストが掛かります. 従って, グラフィックの描画をできるだけ少ない手数で行うように工夫します. 例えば複数のパス図形を逐次描画するのではなく, 一回のメソッド呼び出しで描画できないか検討します. Path2Dオブジェクトを用いて図形を再利用するのも効果的です.

グラフィックの再描画範囲を限定する

canvas要素に対する描画処理の大部分はピクセルデータに関わるメモリ操作であり, 全体に亘って値を書き換えるより必要な部分のみを編集したほうが効率的です. 従って予め書き換えた領域を記憶しておき次フレームの描画時に限定された範囲のみのクリア・書き換えで済むようにします.

アンチエイリアスの発生を抑える

strokeが複数の画素にまたがる場合やdrawImageメソッドの引数に1px以下のサイズが指定された場合, 自動的にアンチエイリアシングが働きます. アンチエイリアシングは本質的にコストの高い処理であるため, 処理上無視できるのであれば, 事前に小数値を調整するなどして描画対象のグラフィックがピクセル境界に収まるようにします. 同様にimageSmoosingEnabledをfalseとして画像スケール時の処理を省くのも効果的です.

合成・フィルタ処理を避ける

影などのフィルタが掛かっている画像はフレーム描画処理の都度生成するのではなく, 一旦別のcanvas等に処理結果をとっておき使い回すようにします.

stateの変更回数を減らす

fillStyleやstrokeStyle等のコンテキストオブジェクトのプロパティを変更する操作は意外にコストが高いものです. 従って描画スタイルの変更がループ処理の内部に存在する場合はその処理をループの外に移動できないか検討します. また, translateメソッドによる平行移動, scaleメソッドによる拡大, rotateメソッドによる回転処理は事前に描画メソッドに渡す座標データを変換しておくことで対応できます.

アルファチャンネルを無効化する

描画対象のcanvasグラフィックに透過処理が必要ない場合は, 明示的にアルファチャンネルを無効化(getContextメソッドに{alpha: false}スイッチを渡す)することで若干の処理速度の向上が見込めます.

will-changeプロパティを設定する

CSSのwill-changeプロパティにcontentsを設定することで, 当該要素が今後アニメーションすることをブラウザに通知し, スクリーン描画の最適化処理ににヒントを与えられます. しかしどのような最適化が為されるかはレンダリングエンジン毎に異なるため, 必ずしも動作速度が改善するわけではありません.

以上まとめると次のようになります.

この他, 一般的に言われているパフォーマンスチューニングテクニックはcanvasアニメーションにおいても有効です. 一方, canvasアニメーションのパフォーマンスを改善する場合, 後からスクリプトの構造に手を入れるのはなかなか難しい作業です. ですから, 面倒であっても最初にどのようにアニメーションを実現するかについての大まかな設計を行いましょう. その上で達成すべき目標(フレームレート等)を立て, 少しずつ実装していくことをおすすめします.

canvasアニメーションを動画として扱う

canvas要素で生成したアニメーションは一般にそのcanvas要素でのみ確認可能です. が, 現在検討されているWebRTC及びMedia Capture from DOM Elementsを用いると, canvasアニメーションを動画(ストリーミング)として扱うことができます. 環境さえ許せばPC間でのアニメーション共有ですら可能ですが, ここではブラウザ内で完結するvideo要素への転送とアニメーションの録画機能についてのみ述べます.

canvas要素の内容をvideo要素に転送する

canvas要素のcaptureStreamメソッドを実行するとCanvasCaptureMediaStreamオブジェクトが得られます. このオブジェクトをvideo要素のsrcObjectプロパティに渡すことでcanvas要素の描画状態をvideo要素に転送することが可能です.

HTMLCanvasElement.captureStream(rate)
ストリームオブジェクト(CanvasCaptureMediaStream)を生成する. 引数にはフレームレートを指定する.
HTMLMediaElement.srcObject
再生対象のストリームオブジェクトを取得・設定する.
CanvasCaptureMediaStream
canvas要素をキャプチャするストリームオブジェクト. MediaStreamオブジェクトを継承している.
CanvasCaptureMediaStream.canvas
キャプチャ中のcanvas要素を取得する.
CanvasCaptureMediaStream.requestFrame()
実行時のcanvas要素内の描画内容を強制的にストリームに出力する. グラフィック書き換えのタイミングで実行すると効率が良いかもしれない.

以前見たとおりcanvas要素はvideo要素の映像を取り込むことができます. 従ってCanvasCaptureMediaStreamと組み合わせると, video要素の映像をcanvas要素で加工し, その結果を別のvideo要素に出力することも可能です. 但し本格的なオーサリングツールとしての活用は相応のマシンパワーが要求されるなど実用には厳しいかもしれません.

ストリーミングの記録

CanvasCaptureMediaStreamオブジェクトはMeidaRecorderオブジェクトでBlob(video/webm)形式に変換可能です. 得られたBlobオブジェクトはURLオブジェクトでurl文字列に変換することで, videoオブジェクトで録画結果を再生したり, a要素を使ってダウンロードできます.

MediaRecorder(stream)
ストリームオブジェクトを録画するためのレコーダを生成する.
MediaRecorder.start()
録画を開始する.
MediaRecorder.stop()
録画を終了する.
MediaRecorder.ondataavailable
録画結果のデータ出力が整った際の処理を設定する. 引数として与えられるBlobEventのdataプロパティが録画結果のBlobオブジェクトにあたる.
[保存]

アニメーション出力方法・まとめ

以下にアニメーション出力方法についてまとめましょう. 新たに追加された仕組みにより, 今後下に掲げる3つの方法でcanvasアニメーションを出力可能です.

  1. canvas/canvas
    通常のcanvas要素でアニメーションを作成し, スクリーン上のcanvas要素に表示する方法. これまで広く使われてきた方法で, 広範な環境で動作します.
  2. video/canvas
    通常のcanvas要素でアニメーションを作成し, スクリーン上のvideo要素に表示する方法. streamを介するため動作環境が限られます.
  3. canvas/offscreenCanvas
    offscreenCanvasでアニメーションを生成し, スクリーン上のcanvas要素に表示する方法です.

実際にフレームレート等を計測したわけではありませんが, 2のvideo/canvasの方法は1のcanvas/canvasよりも若干CPUの負荷が軽減されるようです. また仕様が未確定なものの, 3のOffscreenCanvasを用いた方法は画像転送時に余計な処理が入らないことから, パフォーマンス面に優れることが想定されます.

SVG(scalable vector graphics)との連携

SVGとは

SVGはXMLで定義されている二次元ベクタグラフィック記述言語であり, SVGで記述されたグラフィックはcanvas要素と同様に現在ほとんどのWEBブラウザで表示することができます. (本文書の説明図はこのSVGを用いています. )SVGを用いたグラフィックの例を示します.

svgによるグラフィック
SVGグラフィックの例

SVG画像はJPEG形式やPNG形式のラスタ画像と同様にcanvas要素に描き込むことができます.

動的なSVGをcanvas要素に描画する流れ

SVGはテキストで表されているため, 容易に中身を書き換えることが出来ます. そこで動的にSVGのソースコードを生成し, その内容をcanvas要素に描くことを考えます.

SVGのソースコードの入手方法としては概ね次の2つがあります.

この操作で得られたSVGソースコード文字列をデータURIスキーム(もしくはBlobURIスキーム)形式に変換しimg要素に読み込ませることでSVGグラフィックをcanvas要素に描くのです.

SVGの機能をcanvasに応用する

前述したようにSVGには様々な機能が備わっていて, canvas単体では実現が面倒な処理であってもSVGであれば簡単に記述することが出来る事も多いものです. 従ってSVGをマクロ的な用途で用いるとcanvasの表現力が格段に向上します.

SVGを介したHTMLの描画

SVGにはforeignObject要素というXHTMLやMathMLで定義された要素を埋め込むための仕組みが存在します. この仕組みを利用するとHTMLの内容をcanvasに描画することができます. 例として下のテーブルをcanvasに描画してみましょう.

table in canvas
ヘッダヘッダヘッダ
111213
212223
313233
414243
HTMLをPNG形式に出力した結果

スクリーンに表示しているスタイルはSVG画像化する際に失われてしまうので, SVG内部のstyle要素として補っておく必要があります.

filterプロパティを用いずにSVGフィルタを利用する

canvas要素で描いた内容をtoDataURLメソッドを用いて一旦SVGファイルに埋め込み, それを再度canvas要素に書き戻すことでSVGフィルタをcanvasグラフィックに適用可能です. この方法はorigin-cleanフラグを汚染しないため, toDataURLメソッドやtoBlobメソッドを実行してもセキュリティエラーとなりません.

transform3dを使った画像の変形

現在CanvasRenderingContext2Dオブジェクトに座標の3D変換に関わるAPIは備わっていません. 従って画像に遠近法による変形を施すにはCSS transformによる変形を施した画像をSVG経由でcanvasに書き込みます.

描画範囲をclipPath要素でクリップする

canvas要素に描画した内容をそのペイント領域でクリップすることで, イベントの発生範囲の制御が行えます.

SVGのclipPath要素はcanvas要素におけるclipメソッドと役割が異なります. SVGのclipPath要素をCSSのclip-pathプロパティから参照すると, 描画範囲がそのclipPath要素の内容に狭められると共にイベントなどの発生もその範囲に制限されます.

SVGDOMを利用する

上記はSVGグラフィックそのものをcanvasに描画する例でしたが, SVGDOMやSVGの機能そのものを直接canvasでの描画処理に応用することもできます.

SVGパスデータ文字列を使ったパス図形の定義

Path2Dオブジェクトを使うことでSVGパスデータ文字列を使ったグラフィックの描画が可能です.

SVGPathElementを用いたパス計算

SVGのSVGPathElementにはHTMLCanvasElementには存在しない様々なベジェ曲線やパス図形に関わるユーティリティーメソッドが提供されています. 従ってこれらをcanvas要素でのグラフィック描画に活かすことで表現の幅を広げることが可能です. 下の例ではgetPointAtLengthメソッドを使ってベジェ曲線上の特定の長さの位置に印をつけています.

パスに沿ったテキストの描画

先ほどの応用で, 1文字ずつ描画位置を決定することでパスに沿ったテキストの描画が可能です.

SVGにおけるcanvas要素

先ほど見たとおり, SVGではforeignObject要素を介してHTMLの要素を挿入可能で, これはcanvas要素においても有効です. 従ってJavaScriptの動作が可能な環境であれば, SVGグラフィックの一部をcanvas要素に任せることが可能です. 但しその動作は限定的で, SVGの全ての要素がcanvas要素に作用するとは限りません. 以下に例を示します.

SVG2ではSVG環境でのcanvas要素の振る舞いがグラフィックを表すものとして明確化されました. 従って次のSVGコードはSVG2仕様においては意味のあるものとなります.

canvas要素とイベント

canvas要素が発生するイベント

canvas要素も一般のHTMLノードと同様に各種イベントを発生しますが, focus/blur等のフォーカスに関わるものや, keydown/keyup/keypress等のキー入力に関わるイベントの発生についてはtabindex属性を要します.

なお, canvas要素の子孫にinput要素や(有効な)a要素が含まれていた場合はイベントバブリングによりcanvas要素にもイベントの発生が通知されます.

この動作は後述するcanvas要素におけるアクセシビリティに関わるものです.

カーソル座標とcanvas座標

canvas要素をユーザーインターフェースに利用する場合, しばしばマウス等のポインター座標によるグラフィック操作が発生します. その為,mouseEvent / touchEventから取得したカーソル位置をcanvas要素上の座標に読み替える必要がありますが, この方法には色々なものがあり, WEB標準を考慮するのであればCSSOM Viewの仕組みを用いるとよいでしょう.

なおこの方法はcanvas要素の描画位置が不定な場合の対処法であり, canvas要素の位置が固定されているなら座標算出処理は単なる加減算となります. 同様にcanvas要素に関わる部分をiframe/object要素化し, カーソル座標の基準を書き換えてしまう方法もあります. いずれも負荷の高いgetBoundingClientRectメソッドの実行を避けることが出来ます.

座標変換が必要な場合

グラフィック描画の都合上座標変換が必要な場合は, 現在の変換行列の逆行列を求めて, canvas要素上の座標をcanvasグラフィック内部の座標に変換します. なお, 現状のcanvasAPIではtransform行列や逆行列が得られないので, SVGDOMの仕組みを利用する必要があります.

アニメーション機構をイベント処理に応用する

イベントドリブンなスクリーン書き換えはその発生頻度を考慮しないと著しいパフォーマンス劣化を招きます. そのため, canvasアニメーションでののテクニックを応用することで, 必要最低限度のスクリーン書き換えで済ますことを考えます. 以下に手順を示します.

  1. 処理の開始を宣言する.
  2. requestAnimationFrameメソッドを使って定期的にパラメータの変更有無を確認し, 変更されていた時に限りグラフィックを描画する.
  3. イベント発生時にはグラフィック描画のためのパラメータのみを書き換える.
  4. 処理を終了する.

先程の例のようにマウス位置にグラフィックを描く場合, 素朴にはmousemoveイベントでcanvas要素を書き換えますが, それでは書き換え頻度が過剰です. そこでmouseenterとmouseoutイベントをグラフィック描画処理の開始/終了指示に, mousemoveイベントをグラフィック描画に必要となる座標パラメータの書き換えに割り当てて, 実際のグラフィック書き換えはrequestAnimationFrameメソッドに任せています.

ペイント状況に応じたイベント伝播

canvas要素はペイント状況にかかわらずカンバス領域全体がカーソルイベントを発生させます. その為, グラフィックが塗られている部分でのみcanvas要素特有のイベント処理を行わせたい場合は, カーソル座標からピクセルの色(アルファ値)を取得し, その内容を元にcanvas要素直下のノードにイベントを伝播するか判定します.

  1. canvas要素直下のノードにイベントを伝播するには, まず自身が発生させたイベントを無効化します. e.stopPropagationメソッドを実行することでDOMツリー上の親要素へのイベント伝播を阻止できます.
  2. その後(レイアウト上の)canvas要素直下のノードを取得します. このノードはcanvas要素にスタイル値pointer-events:noneを設定し, その後でdocument.elementFromPointメソッドを実行することで得られます.
  3. 得られたノードに対してElement.dispatchEventメソッドを実行しイベントを発行します.
  4. 最後にpointer-eventプロパティを元の内容に戻します.

複数のcanvas要素が重なっていた場合

複数のcanvas要素が重なっていた場合も同様の仕組みで対処できます.

処理シーケンスを示します. dispatchEventが次のcanvas要素のclickイベントを発生するため, イベントがまるでcanvas要素の重なりを掘り進むように伝播していくことが判ります.

重なったcanvas要素間のイベントの伝播

ペイント機能の実装

canvas要素をペイントツールに利用する場合, マウス等のポインターの動きをパス図形の定義に割り当てます. 以下に最も単純なサンプルを示します.

筆圧を加味した“stroke”

ペンによるポインターイベントではカーソル座標の他に筆圧に相当するpressure値が得られます. この値を線の太さに割り当てる(可変幅ストローク)ことでより自然な使い勝手となります. しかしstrokeメソッドによる描画では線の太さが均一となるため, パス図形を使ってストローク図形を定義します.

まず筆圧値が変化する前後での座標を中心とした2つの円を考えます. これらの円を連結するように図形を定義することでストローク幅が変化する様子を表現します.

基本的な可変幅ストロークの考え方

パス図形と座標の包含関係の判定

現在カンバスが保持しているパス図形と座標(≒canvas要素の画素位置)の包含関係を確認できます. 例えばある領域にマウスカーソルが当たった際にカンバスの内容を書き換えると言った用途に使えます.

CanvasRenderingContext2D.isPointInPath(x, y[, fillRule])
CanvasRenderingContext2D.isPointInPath(path, x, y[, fillRule])
指定した点が現在のパス図形の領域内か判定する.
CanvasRenderingContext2D.isPointInStroke(x, y)
CanvasRenderingContext2D.isPointInStroke(path, x, y)
指定した点が現在のパス図形のストローク領域内か判定する.

例を示します. この例では内部の矩形にマウスカーソルを当てることで, 塗り潰しの色・線の色を変化させています.

Path2Dオブジェクトと組み合わせる

Path2Dオブジェクトをサポートする環境であれば, パス図形毎に領域判定が可能となります.

アクセシビリティ

アクセシビリティとは

アクセシビリティとはユーザーや環境を問わずに伝えたい内容(コンテンツ)を相手に伝達できるかどうかを表す尺度です. WEB環境において, 単にHTMLで記述しただけのコンテンツは「本質的に伝えたい内容」をHTML形式でマークアップしたに過ぎず, 受け手の条件(視聴覚条件, 年齢条件)・環境(スクリーン, OS, ブラウザ)によっては正しく解釈されるとは限りません. 従ってより広範な環境に正しく伝わる・動作する(アクセシビリティを確保する)ための下準備をする必要があります.

アクセシビリティとフォールバック

外部リソースを参照したりマルチメディアに関わる要素については, あらゆる環境で有効に動作するとはわけではありません. 例えばスクリーンリーダー環境下においては視覚的なコンテンツは意味を持ちません. そのためこのような要素については, 予めそこにどのようなデータが存在するのかを記述しておくことが推奨されています. この記述をフォールバック(代替)コンテンツと呼び, 元となるコンテンツを表示・出力できない場合にその代わりとして表示されます. こうすることで, (100%ではないにせよ)制作者が意図した内容が相手に伝わりやすくなり, つまりアクセシビリティが改善します.

フォールバックコンテンツの指定が可能な要素にはimg要素(alt属性にフォールバックコンテンツを設定する. ), object要素, iframe要素がありますが, canvas要素においてもその子要素にフォールバックコンテンツを設定することが可能です. その際, フォールバックコンテンツとして配置可能な内容としては静的な代替画像, 描画した内容についての説明, 描画した文字列のほか, canvas要素だけの特徴としてフォーカスを受け取ることが可能なノードが挙げられます. つまり, 条件付きながらa要素によるリンク, input/select要素, tabindexを指定した要素を配置できます.

canvas要素におけるフォールバックコンテンツ表示の条件

canvas要素におけるフォールバックコンテンツが表示される条件は次の何れかを充たす場合です.

フォールバックコンテンツとの連携

一般的なフォールバックコンテンツは, それを囲む要素が有効となる環境であれば通常スクリーンに表示されません. しかしcanvas要素においては非表示であるにも関わらずフォールバックコンテンツへのフォーカスの移動やキーボードによる値の変更が可能です. この動作をインタラクティブなフォールバックと呼びます.

なぜこのような挙動を取るのかについては明確な理由があります. それは先ほど見た「HTML文書が提供する内容は(canvas要素の有効・無効に関わらず)本質的に同じであるべき」という原則です. つまりcanvas要素の有効無効は, 同じ内容を(より)グラフィカルに表現するか否かの差に過ぎず, 機能的には全く同等であるべきなのです. 従ってフォールバックコンテンツにリンクやinput要素等のフォーカス移動が可能な内容が存在する場合は, canvas要素が有効な環境においてもあなたが責任を持って同等の機能を実装すべきということになります.

これは言い換えるとcanvas要素を使って独自のカスタムコントロールを作ることが出来るということです. もちろんイベント処理などの基本的な仕組みはそのまま流用可能です. また, canvas要素が無効な環境ではHTML標準のコントロールにフォールバックされるわけです.

この観点からcanvas要素のAPIにはこのUI機構の実装をサポートするための仕組みが提供されています. 以下はcanvas要素を用いてフォーカス移動処理を実装したものです.

このカンバス要素には 四角三角 の3つの図形が描かれています.

フォールバックコンテンツのスタイルを参照する

canvas要素配下のフォールバックコンテンツは直接描画されないだけでDOM内部で生きています. 従ってグラフィックを描画する際に直接色を指定するのではなく, フォールバックコンテンツの内容を参照するようにすると, 文書全体の印象をcanvas要素の有効/無効に依存せずCSSのみで制御することが可能となります.

div in canvas

カスタムコントロールの構築

先ほど見たとおりフォーム部品をcanvas要素で囲んでカスタムコントロールとすることが出来ます. canvas要素が動作しない環境でも元のフォーム部品が表示されることでWEBページの機能が維持されることに着目しましょう. 下の例ではcanvasをクリックし, 矢印キーを使って値を変更すると, その内容でグラフィックが書き換わります.

フォーカスリングの描画

コントロールにフォーカスが存在していることを表すにはグラフィックの内容を書き換えただけでは不十分です. drawFocusIfNeededメソッドを用いてフォーカスがどこにあるのかをブラウザ側に伝える必要があるからです. こうすることでフォーカスがあたった際の自動スクロール等が動作するようになります.

出典:canvas要素のフォーカス領域を指定する
動作サンプル:Accessible canvas clock※ですが, 現在はAPIが古くなっており動作しません.
CanvasRenderingContext2D.drawFocusIfNeeded()
フォーカスリングを描画する. 引数にElementを指定すると, その要素がフォーカスを持っている場合に, 現在のパス図形に沿ってフォーカスリングを描画する.

ヒット領域の設定(tobe)

ヒット領域はcanvasグラフィックにおける構造を表す概念で, canvas要素上で発生したイベントをその発生位置からヒット領域ごとに振り分けるために用います. ヒット領域はフォールバックコンテンツと結びつけることでイベントを直接内部に伝達することも出来ます. 下記に概念図を示します.

ヒット領域の概念

このようにもともとフラットなcanvasグラフィックにDOMに似たツリー構造を持たせることが可能となります.

CanvasRenderingContext2D.addHitRegion(params)
ヒット領域を追加する. カンバスに擬似的に子要素として振る舞う領域を追加する. 引数にはパラメータセットを指定する.
path
ヒット領域とするパス図形. 省略するとコンテキスト中のパス図形を用いる.
fillRule
ヒット領域の定義ルール. nonzero/evenoddの何れかを指定する.
id
ヒット領域に対する識別子. canvas要素の当該ヒット領域上でMouseEvent/TouchEventが発生した際, イベントオブジェクトのregionプロパティにこの値が設定される. 未指定の場合, 下記のcontrolを指定する必要がある.
label
ヒット領域に対応するcontrolが存在しないケースに対するヒット領域に対するラベル(ARIA role).
role
ヒット領域に対応するcontrolが存在しないケースに対する視聴覚ロール(ARIA role)
parentID
ヒット領域に対する親ヒット領域のIDを指定する. (ヒット領域を階層化できる)
control
ヒット領域に対応する要素. canvas要素配下のフォームオブジェクトに限られる. ヒット領域上で発生したイベントはcontrol要素上で着火したものとして扱われる.
cursor
ヒット領域上でのマウスカーソルの種類.
CanvasRenderingContext2D.removeHitRegion(id)
指定したidの要素に対するヒット領域を削除する.
CanvasRenderingContext2D.clearHitRegions()
ヒット領域を全て削除する.
MouseEvent.region/TouchEvent.region
イベントを発した領域が設定されている.

ヒット領域が定義されたcanvas要素では, その上で発生したMouseEventのregionプロパティにヒット領域のIDが設定されます.

IDの代わりにcontrolを指定しておくと, イベント発生時にcontrolでイベントが発生したものとして扱われます.

WEBページの自動スクロール

scrollPathIntoViewメソッドを用いるとWEBページを自動的にスクロールさせ, 当該パス図形範囲をスクリーン上に表示させます. これはフォーカスが当たった際の動作をcanvas内の図形において再現します.

CanvasRenderingContext2D.scrollPathIntoView()
CanvasRenderingContext2D.scrollPathIntoView(path)
現在のパス図形を表示できる位置までWEBページをスクロールする.

描画処理のコンポーネント化

グラフィック描画処理をまとめる

グラフの出力やブログパーツと言ったWEBページ横断的に利用可能なグラフィック処理は, 予めコンポーネント化しておくことで後々の管理がやりやすくなります. 以下に代表的なコンポーネント化の手法を示します.

グラフィック描画を外部ページに任せる

グラフの出力やブログパーツなど汎用的に利用可能なグラフィック描画機能は, 独立したWEBページ(コンポーネント)にまとめることで複数のページから呼び出し可能となります. その際, 処理を行うために必要となるパラメータはpostMessageメソッドを使って当該WEBページに引き渡すようにします. コンポーネント側ではmessageイベントを使って処理の呼び出しを監視します.

postMessageの引数としてメッセージ送信可能なオブジェクトには条件があり, 容易に複製が得られるものに限られます. 例えば, 文字列・数値等のネイティブ型, ArrayBuffer/ArrayBufferViewと言ったデータコンテナ, 及びそれらをまとめたObject型が, またcanvas要素に関わるものであればImageDataオブジェクトが送信可能です. 逆に内部状態を持っていて容易に複製出来ないもの(HTMLElementやCanvasRenderingContext2D等)をpostMessageに渡すとエラーとなります. 例を示します.

この方法の優れている点はクロスドメインでのコンポーネント呼び出しが可能な点です. つまり, コンポーネントをWEBサーバーに配置してしまえばどこからでもその機能を呼び出せるのです.

Web Components仕様を用いる

Web ComponentsはWEB部品を定義するための仕様群を指し, 大きく分けてtemplate要素HTML ImportsShadow DOMCustom Elementsから構成されています. ここではCustom Elements仕様を使ってcanvas要素を拡張したmy-img要素を構成する例を示します.

高解像度環境での動作

出典:High DPI Canvas

canvas要素とスクリーン解像度

Retinaディスプレイのようにスクリーン環境の機能が向上するにつれ, WEBでのピクセル値の扱いも変化しています. つまり1px平方を複数のスクリーン画素によって描くことで, これまでよりも精細なグラフィックが描けるようになりました. その反面, canvas要素はサイズをピクセル値で指定するために互換性の点で非常に厄介な問題が発生しています.

デバイスピクセル比とcanvas内部ピクセル比

高解像度環境でcanvas要素を扱う場合, デバイスピクセル比とcanvas内部ピクセル比の2つを考える必要があります. 前者は1ピクセルあたりのスクリーンデバイス上での画素数を表し, 後者はcanvasグラフィックの内部で1ピクセルあたりに確保される画素(グラフィックの最小分解単位)数です. これらは次のAPIで取得できます.

window.devicePixcelRatio
デバイスピクセル比. undefinedの場合は1(devpx/px)
CanvasRenderingContext2D.webkitBackingStorePixcelRatio
canvas内部ピクセル比. Safari専用(bspx/px)

この内容は環境(OS,ブラウザ)で大きく異なるので, 同じスクリプトを発行したとしても, 必ずしも環境毎に最適化された出力が得られません.

ピクセル比を加味したグラフィックの描画

canvas要素で描いたグラフィックが最も美しくスクリーン上に表示されるには, canvas内部の1画素がスクリーン上の1画素に対応する場合です. そのため事前に絶対ピクセル比(canvas最小分解単位あたりのデバイスピクセル数)を求めておき, その結果に応じてカンバスサイズを広げ, scaleメソッドを使って座標スケールを調整しておきましょう. こうすることでメインとなる描画処理に一切手を入れること無く, 動作環境毎に最適なグラフィックを描くことが可能です.

高解像度ピクセルデータの取得

Safari環境ではwebkitBackingStorePixcelRatioに応じて1ピクセルが複数の画素データをもつため, 通常のgetImageDataではなく高解像度環境専用のピクセルデータ取得APIが定義されています.

CanvasRenderingContext2D.webkitGetImageDataHD()
高解像度ピクセルデータを取得する
CanvasRenderingContext2D.webkitPutImageDataHD()
高解像度ピクセルデータを挿入する

CSSとcanvas

canvas要素と背景

canvas要素にも背景画像を定義することが出来ます. つまり, canvas要素は実質静的なスクリーンと動的なスクリーンの2層構造をとります. 従って静的な部分をCSS背景画像としておき, (アニメーション等の)可変な部分のみをcanvas要素本体に描画すると言った使い分けをすることで, グラフィック描画部の構造がシンプルに保たれます.

canvasグラフィックを背景に用いる

先の概念を一歩進め, canvas要素で描いた内容を何らかの要素の背景とすることを考えます. この場合, グラフィックの内容を一旦データURIスキーム形式もしくはBlobURIスキーム形式としてCSSプロパティに静的に設定する必要があります. しかし, ブラウザによっては独自機構を用いる, もしくは次期CSS仕様を先取りすることで背景画像に直接canvas機構で描いたグラフィックを設定することが可能です. 何れにせよ標準仕様が現在策定中であるため, 使う場合は注意しましょう. (Internet Explorerにはこれらに類するものは存在しません.)

CSS背景によるcanvasグラフィック表示のメリット

CSSとcanvas要素とを組み合わせると次のようなメリットが得られます.

element関数+CSS.elementSourcesを用いる(w3c標準)

background-image:element(#id)
CSS4のelement関数を用いて指定したIDの要素のグラフィックを背景画像とする.
CSS.elementSources.set(id, element)
DOMオブジェクトをグラフィック参照先として登録する. CSS4で検討されている.

Gekko系ブラウザ(FireFox等)

background-image:-moz-element(#id)
指定したIDの要素のグラフィックを背景画像とする(CSS4のelement関数に相当する). 従って, canvas要素を参照することで動的な背景とすることが可能となる.
document.mozSetImageElement(id, canvas);
documentにCSSの-moz-element関数中から参照可能なHTMLCanvasElementオブジェクトを追加する. FireFoxの独自機能.

旧webkit系ブラウザ(Safari等)

background-image:-webkit-canvas(id)
背景画像をcanvasによるグラフィックとする. idは下記メソッドにおける識別子を指定する.
document.getCSSCanvasContext(type, id, width, height)
内蔵canvasに対応するコンテキストオブジェクトを取得する.

CSS Shapesとcanvas要素

CSS ShapesはCSSで扱う図形の概念で, テキストの回り込みと言った用途に用いられます. しかし標準的な図形が矩形, 楕円, 多角形に限られており表現力に欠けます. 一方, 図形の指定に画像データを渡すことが出来るため, canvas要素で描いた図形を元に自由なテキストレイアウトが可能です.

shape-outside
要素の外形を指定する. floatプロパティと組み合わせることで, テキストの回り込みを自由に設定できる. url関数を使って画像データを渡すことが出来るのでtoBlobメソッドを使ったcanvasグラフィックを渡すことで任意のテキスト回り込みを定義できる.
このdiv要素にはcanvas要素で描いたグラフィックによるテキストの回り込み設定が施されています. shapeOutsideプロパティにtoBlobで得たグラフィックを設定することで基本図形だけでは表現出来ない自由なテキストレイアウトを実現しています. (Chromeのみで動作を確認)
canvas要素を使ったテキスト回り込み

canvas要素によるリソースの占有

ポインターイベントとcanvas要素

マウスカーソルが発するポインターイベントは通常スクリーン上のカーソル位置を規準に生成されます. そのため, canvas要素が受け取れるイベントはcanvas要素の描画範囲に限られます. 一方でcanvas要素は逐次内容を書き換えることで内部で無限の広がりを表現することが出来るため, 単一方向への継続的な移動と言った操作が発生します. 例えば地図の表示範囲を変更する操作がこれにあたります. 従って先の範囲限定的なイベント取得ではcanvas要素からカーソルが外れることで操作が途切れてしまうし, フルスクリーン環境ではスクリーン端にカーソルが到達したらそれ以上移動できないということになります.

この問題を解決するには幾つか方法がありますが, ここではWEBブラウザが提供しているPointer Lock APIを用いてcanvas要素がマウスカーソルを占有する方法を示します.

Pointer Lock APIとは

Pointer Lock APIは特定のHTML要素に対してポインターイベントを占有させる仕組みで, カーソル位置の代わりに新たにカーソルの移動量を取得可能とします. そのため, ノードの描画位置やスクリーン範囲等の境界値に依存しない継続的なイベント検知が可能です. なおカーソルの位置の管理をポインター占有側に移譲するため, 副次的にブラウザによるカーソルは表示されなくなります. これらの特性から事実上canvas要素のための仕組みと言って良いと思われます.

element.requestPointerLock()
ポインターの占有(ポインターイベントの発生箇所の固定)を開始する. 占有を解除するにはESCキーを押下するもしくはexitPointerLockメソッドを実行する. ポインター占有中はclientX/Y等のカーソル座標の内容が固定される(意味がなくなる).
document.pointerLockElement
ポインターを占有している要素を取得する.
document.exitPointerLock()
ポインターの占有を終了する.
document pointerlockchangeイベント
ポインターの占有状況が変化した際に発生する.
document pointerlockerrorイベント
ポインターの占有操作に失敗した際に発生する.
mouseevent.movementX/movementY
ポインターの移動量(前回のmousemoveイベント発生時からの差分値)を取得する.

例を示します.

canvas要素のフルスクリーン化

ポインターの占有と同様にFullscreen APIを用いると特定の要素をスクリーン全体に広げて描画することが可能です.

element.requestFullscreen()
当該要素をフルスクリーン化する.
document.fullscreenElement
フルスクリーン化している要素を取得する.
document.fullscreenEnabled
フルスクリーン化可能かどうかを判定する.
document.exitFullscreen()
フルスクリーン動作を終了する
document fullscreenchangeイベント
フルスクリーンの状況が変化した際に発生する.
document fullscreenerrorイベント
フルスクリーン操作に失敗した際に発生する.

一般にスクリーンのサイズは環境によって異なるため, canvasサイズの調整が必要となります. 従ってフルスクリーン化前後でグラフィック内容を維持する場合はどのように実現するかが問題となります.

例を示します.

なお, canvas要素そのものではなく, フルスクリーンを解除するコントロールとcanvas要素とをひとまとめにしたものをフルスクリーン化すべきです. なぜなら, キーボードによるフルスクリーンの解除はタブレット等のキーボードレスの環境で問題となりうるからです(ソフトウェアキーボードを表示する手間をユーザーに強いるのは不適切です). もちろんアプリケーション側でフルスクリーン解除の仕組みを提供する場合はこの限りではありません.

ブラウザ環境の保護

リソースの強制開放

出典:Canvas Context Loss and Restoration
参考:Canvas が専有するリソースをパージ可能になる Canvas Context Loss and Restoration について

WEBブラウザに表示するコンテンツがリッチになるにつれ, 要求されるハードウェアリソース(CPU/GPU資源やメモリ)の量も年々増加傾向にあります. これはcanvas要素においても例外ではなく高機能・高精細化の代償として, 消費されるリソースもかなりの分量となってきています. とは言え, 全ての環境がWEBページの要求する条件を満たせるわけではありません. そのため高負荷なWEBページを公開した際, スペック的に見劣りする環境からのアクセスを考慮しなかった場合, ブラウザはおろかOSごとクラッシュしてしまうユーザビリティ的に最悪なケースも発生し得ます.

このような問題は一般にメモリ管理の拙さやチューニング不足と言ったスクリプトの質の問題として片付けられてしまうものの, 時には意図しないバグの混入によっても発生します. そのため, 致命的な状態に陥る前に占有しているリソースを強制開放させ, ブラウザ環境を保護することは原因の究明を円滑に行う目的においても理に適っています. もちろんアプリとしての機能は損なわれてしまいますが, OSを巻きこむことに由る不測の事態と比べれば微々たる問題と言えるでしょう.

これはそもそもWebGL環境における要望事項でしたが, blinkはこのアイディアをCanvasRenderingContext2Dにおいても利用可能としています.

ctx.getContext("2d", {storage: "discardable"})
コンテキストを破棄可能とする. システムに負荷がかかった際, 強制的にコンテキストを破棄しcontextlostイベントを発生する.
ctx.getContext("2d", {storage: "persistent"})
逆にリソースの開放を一切しない(つまりこれまでどおりの用途とする)
ctx.isContextLost()
現在のコンテキストオブジェクトが破棄させられているかの判定値. trueの場合, コンテキストオブジェクトに対する一切の操作が不可能となる.
contextlostイベント(ctx.oncontextlost?)
コンテキストオブジェクトが破棄された際に実行する処理を設定する.
contextrestoredイベント(ctx.oncontextrestored?)
コンテキストオブジェクトが復帰した際に実行する処理を設定する.

シングルWEBページアプリケーションとメモリリーク

ブラウザ環境の変化はWEBページの利用にも微妙な変化をもたらしました. これまで使い捨てていたページ読み込み処理は, JavaScriptやCSS等のロードすべきモジュールの増大に伴いもはやコストの高い操作となりました. そのためデータの読み込みはなるべくAjaxで行い, 単一のWEBページの生存期間をできるだけ長くする手法(シングルWEBページアプリケーション)が広まりつつあります.

それに伴いスクリプト処理においても注意すべき点が出てきました. その最たるものがJavaScriptにおけるメモリリークの問題です. メモリリークそのものはあってはならないバグの一つですが, これまではWEBページの廃棄と共にメモリが開放されるためさほど重要視されませんでした. しかしWEBページの寿命が長くなると些細なリークであっても積み重ることで深刻な問題を引き起こす恐れがあるのです.

canvas要素においてメモリリークを引き起こす可能性がある部分は次のとおりです. ページロード時から徐々にメモリが消費されていく様子が確認できた場合, このような不具合が隠れていないか確認して下さい.

メモリの再利用によるコスト削減

メモリリークに注意すると共に, そもそものメモリ消費量を抑えることも重要な課題です. canvas要素で描いた内容を複数箇所に表示する場合, 複数のcanvas要素を用いるのではなく使用するオブジェクトを工夫することで消費するメモリの量を抑えることができます.

Blobオブジェクトによるグラフィックの共有

複数箇所に同一のグラフィックを描く場合, 単一canvas要素で描いた内容をBlobデータとしてimg要素に設定します.

ImageBitmapオブジェクトによるグラフィックデータの参照

ImageBitmapRenderingContextオブジェクトを用いると単一のImageBitmapを複数のcanvas要素から参照することが可能です.

canvasアニメーションをvideo要素に出力する

canvasアニメーションをcaptureStreamメソッドを用いてvideo要素に表示するとグラフィック操作に伴うコストが抑えられます.

ArrayBufferオブジェクトを再利用する

ImageDataオブジェクトの生成にcreateImageDataを用いる代わりに, ArrayBufferオブジェクトからImageDataオブジェクトを直接生成するようにするとメモリを再利用することができます. ImageDataオブジェクトの開放に伴うガベージコレクションが悪影響を及ぼしている場合に有効な対策です.

WebGLとの連携

CanvasRenderingContext2DとWebGLの比較

CanvasRenderingContext2D(ctx2D)とWebGLRenderingContext(WebGL)とは同じcanvas要素にグラフィックを描くとは言え, その得意としている分野は異なっており自ずと使い分けが必要になります.

WebGLの特徴

なおcanvas要素に描かれた内容はctx2D, WebGL相互に再利用することが可能です.

WebGLによる遠近法変換

ここではWebGLとの連携の例として遠近法変換によるグラフィック変形のサンプルを挙げます. わかりやすさを優先しているため, 余計な処理は省いています. なお詳細な解説は割愛します.

Tips

汎用グラフィック処理への応用

node-canvasによるグラフィック操作

node-canvasはNode.js環境で動作するcanvas要素互換のベクタグラフィック描画ライブラリです. Node.jsはJavaScriptコードの実行環境の一つで, WEBブラウザから独立して動作する特徴を持ちます. そのため, JavaScriptコードをShellスクリプトやコマンドスクリプトと同等のアプリケーション処理に利用できる他, WEBサーバーとして実行(サーバーサイドスクリプト)として実行することも可能です.

下記はnode-canvasを使った画像ファイルの生成例です. WEBブラウザと動作環境が異なるためファイル操作APIに違いがあるもののcanvas操作においてはさほど違いはありません. このようにnode-canvasを利用するとcanvasスクリプトをより汎用の画像処理に応用することが可能です.

node-canvasの導入

以下はUbuntuでのnode-canvasの導入手順です. (詳しくはnode-canvasのサイトを参照して下さい)

canvas要素との互換性

node-canvasは基本的にcanvas要素の仕様に則した実装を心がけています. が, 動作環境の違いや実装時期の絡みから細かい部分で実際のcanvas要素とは異なっていたり, 独自の機能を備えています.

WebDriver APIを用いたcanvas操作

上記はcanvas互換の環境によるグラフィック生成ですが, WebDriver APIを用いる方法もあります. WebDriver APIはW3Cで標準化が進められているWEBブラウザを外部プログラム・スクリプトから制御するための仕組み(乱暴に言えばExcelに対するCOMコンポーネント環境に相当するもの)で, 現在主要なWEBブラウザのほとんどで本機能をサポートするドライバソフトウェアが提供されています. 多くはWEBアプリケーションのテスト自動化の目的で導入しますが, 処理の自動化に着目してcanvasグラフィックを介した画像データの生成に応用することができます. いちいちWEBブラウザが起動する煩わしさがありますが, node-canvasでの互換操作とは異なり最新のAPIが利用できる点で有利です.

下記はFirefoxをWebDriver APIで操作した例です.

自動化環境を整える

WEBブラウザを自動化するには通常次の環境を整えます.

  1. 自動化対象のWEBブラウザ
    主要なWEBブラウザであれば大抵自動化可能ですが, 下記ドライバソフトウェアが提供されているかを確認します.
  2. WEBブラウザに対応するWebDriverソフトウェア
    ドライバソフトウェアはWEBブラウザベンダが製品毎に提供しています. この中からブラウザが動作する環境(Windows/OSX/Linux, 32bit/64bit)別の正しいものを用意します.
  3. 自動化プログラムの実行環境
    自動化プログラムの動作環境としてはNode.js, Java, .NET, Ruby, Perl, Python等が挙げられます. なお, WebDriver APIを操作することができれば言語に制限はありません.
  4. WebDriver APIをバインドするライブラリ
    上記動作環境毎に用意されたSelenium WebDriverバインドライブラリを導入します.
  5. 自作の自動化プログラム
    導入したライブラリを元にWEBブラウザを操作するプログラムを記述します.

補足)ExplorerCanvasを利用する

ExplorerCanvasの動作原理

レガシーIE上でHTML5のcanvas要素をエミュレートさせるExplorerCanvasは, 各種メソッドに対する操作をVML(Vector Markup Language)に置き換えています. 従って生成されるグラフィックは厳密にはベクタ形式の画像です. また, ライブラリが作られてから時間が経過しているため対応していないAPIも多々あります.

これらは何れも魅力的な機能ですが, 残念ながらExplorerCanvasでは利用することができません. また, メソッドを実行する毎にDOM上にVML要素に相当するオブジェクトが蓄積していくため, 余り凝ったことをした場合, ブラウザへの負荷が懸念されます. 従ってExplorerCanvasを介したcanvas要素の利用はグラフの描画など, あくまでサポート的な利用に留めておくべきです. この点でどうしてもグラフィカルな処理が必要というのであれば, 無理にcanvasを利用するのではなく, 素直にFlashの利用を検討するなど既存技術をベースに検討したほうが良いでしょう.

ExplorerCanvasの導入

ExplorerCanvasを利用するには他のJavaScriptライブラリと同様, HTMLのhead要素の中で次の宣言をします.

これだけでcanvas要素にアクセスする準備は整いました. 他の環境と同じくgetElementByIdメソッド等でcanvas要素に相当するオブジェクトを取得し, 各種描画メソッドを実行してグラフィックを描くことができます. が, 細かい部分での注意点が存在します. いずれもcanvas要素そのものの操作に関わるものなので, 動的に要素を生成するケースを想定している場合は注意して下さい.

実際の動作についてはこちらを参照して下さい.

参考文献