nmi.jp Twitter → @tkihira
JavaScriptでアニメーションを書く初歩の初歩 JavaScriptの将来と、JSXの関わり

JSXでCanvasを使う方法


2013-12-14
Takuo Kihira

※この記事は、JSX Advent Calendar 2013の一部です。

JSXは型付き言語なので、JavaScriptで簡単に書ける内容が複雑になってしまうこともあります。Canvas周りでは特にその傾向が強いので、ここではJSXでCanvasを扱う書き方を簡単にご紹介します。

まずCanvasの作成です。Canvasの型はHTMLCanvasElementとなります。

var canvas = dom.document.createElement("canvas") as HTMLCanvasElement;

JSXにはグローバル変数は存在しません。JavaScriptのグローバル変数(正確にはグローバルオブジェクトのプロパティ)documentにアクセスするためには、JSXではdomのプロパティとしてアクセスします。dom.window.documentとアクセスしても同じです。

var canvas = dom.id("canvas-id") as HTMLCanvasElement;

もしHTMLに既にCanvasエレメントが存在するのであれば、dom.idで取得することも出来ます。これはdom.window.document.getElementById("canvas-id") と同等の処理になります。

dom.idもdom.document.createElementも、どちらも返り値はHTMLElement型になります。こちらをダウンキャストしてHTMLCanvasElementに変換する構文がas構文になります。JSXをデバッグモードで使用した場合は、このキャストで失敗した場合(たとえばcanvasではなくimgエレメントを取得してしまった場合など)には実行時に例外が投げられるので安心です。リリースモードの場合は型の確認はスキップされます。

さて、これでcanvasが取得できました。次にcontextを取得するのですが、一般にCanvas APIと呼ばれるAPI群の元クラスは、CanvasRenderingContext2Dというものになります。

var ctx = canvas.getContext("2d") as CanvasRenderingContext2D;

これでcontextの取得が出来ます。getContextはObject型を返すので、こちらもダウンキャストが必要です。たとえばもしWebGLを使おうという場合には次のようになります。

var ctx = canvas.getContext("experimental-webgl") as WebGLRenderingContext;

さて、ここまで準備できたら後は普通のJavaScriptと同じようにつかえます。たとえば

var canvas = dom.id("canvas-id") as HTMLCanvasElement;
canvas.width = 300; canvas.height = 300;
dom.document.body.appendChild(canvas);
var ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.fillStyle = "#f00";
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = "#ff0";
ctx.fillRect(100, 100, 200, 200);

このような感じですね。これは以下のJavaScriptのコードと同一のものとなります。

var canvas = document.getElementById("canvas-id");
canvas.width = 300; canvas.height = 300;
document.body.appendChild(canvas);
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#f00";
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = "#ff0";
ctx.fillRect(100, 100, 200, 200);

型が増えているので面倒だと思われるかもしれません。実際コード量が増えているので面倒なのは間違いなく、この程度のプログラムであればJSXを使わない方が良いでしょう。しかし、JSXに型があるおかげで、大規模なプログラムになっても気づきにくいバグが生まれにくくなっているのです。たとえば上の例だと、"canvas-id"がいつの間にかimgに置き換わってしまっても、実行時にすぐに問題が発覚します。ctx.fillStyleをスペルミスしてfillSytleにしてしまっても、コンパイルが通りません。大規模な場合、それが本当に大きなメリットになるのです。