API

A tool that lets a web page have a “drawing area” where JavaScript can draw shapes, text, images, and animations.

Canvas API

Webページの中で自由に絵や図形、文字、画像、アニメーションを描ける“お絵描き用の場所”を作るための仕組みです。

canvas 要素
ページの中に小さな“お絵かきボード”を作って、JavaScriptで図形や文字、画像をその場で描ける仕組み。
fillRect()
キャンバス上に指定した位置と大きさで四角形を描き、それを現在の塗りつぶし色で塗りつぶす機能を提供します。
fillStyle
キャンバス上で描画する図形や文字の色や模様を設定するプロパティです。
getContext()
グラフィックを描画するためのメソッドやプロパティを持つオブジェクトを返す。

Canvas API とは?

Canvas API は、Webページの中に「お絵描き用のキャンバス」を置いて、JavaScriptで絵を描いたり、写真を貼ったり、アニメーションを動かしたりできる仕組みです。

普通の HTML では、四角や丸いボタンのような決まった形しか作りにくいのですが、Canvas API を使うと、自分の好きな絵や形を自由に描けます。

線を引いたり、色を塗ったり、文字を描いたり、画像を読み込んだりできるので、ゲーム画面、グラフ、画像加工ツールなど、いろいろな場面で使われます。

「紙とペンをブラウザに追加して、JavaScriptで描く」と考えるとイメージしやすいです。

とてもシンプルなサンプルコード(四角を描く例)

青い四角を描くキャンバス(視覚的なお絵かき領域)

HTML

<canvas id="myCanvas" width="300" height="150">
	青い四角を描くキャンバス(視覚的なお絵かき領域)
</canvas>

<script>
	const canvas = document.getElementById("myCanvas");
	const ctx = canvas.getContext("2d");

	// 四角を描く(青色で塗りつぶし)
	ctx.fillStyle = "darkblue";
	ctx.fillRect(50, 30, 200, 100);
</script>

このコードをブラウザで開くと、青い四角が表示されます。

canvas がキャンバス、getContext("2d") が「描くための道具を取り出す」イメージです。

簡単なアニメーションのサンプル(丸が動く)

丸が右に動くアニメーションのキャンバス

このコードは「丸が右に動く」アニメーションになります。

ゲームや動くグラフなども、考え方は同じです。

canvas のサイズ

canvas 要素には、width 属性と height 属性が規定されており、それぞれ、canvas 要素で描かれる領域の横幅と縦幅を CSS ピクセルで指定します。

HTML

<canvas width="300" height="150"></canvas>

これだけでは描画領域を設定しただけなのでまだ何も表示されておりませんが、もし指定がなければ、横幅 300ピクセル、縦幅 150ピクセルとして処理されます。

フォールバック

canvas 要素の中には、JavaScript で描かれる図のフォールバック・コンテンツとして用意したコンテンツを入れます。canvas 要素に対応していないブラウザやスクリーン・リーダーなどでは、このフォールバック・コンテンツが利用されることが期待されています。

canvas 要素のコンテンツ・モデルはトランスペアレントです。そのため、canvas 要素の中に入れることができるコンテンツは、該当の canvas 要素の親要素のコンテンツ・モデルに依存します。

canvas 要素の中に入れるコンテンツは、フォールバック・コンテンツとして役に立つようなものを入れるべきです。つまり、アクセシビリティを考慮することが期待されます。

HTML

<canvas width="300" height="150">
	<!-- ここにフォールバック・コンテンツ入れます。 -->
</canvas>

このように、canvas 要素の開始タグと終了タグの中に、フォールバック・コンテンツを入れます。こうすることで、もしブラウザが canvas 要素に対応していなかったり、JavaScript を無効にしていれば、フォールバック・コンテンツが表示されることになります。

では、フォールバック・コンテンツとして何を用意すれば良いでしょうか。フォールバック・コンテンツは、「 canvas 要素を何のために使っているのか」「どこまでフォールバックさせたいのか」「 canvas 要素以外の手段で同じものが実現できるか」「制作にどれだけコストをかけることができるのか」、また「その canvas 要素が表すコンテンツがどれほど重要なのか」などによって、大きく異なってきます。そのため、一概に、こうあるべきだと言い切ることはできませんが、やはり、もし canvas 要素が利用できたなら、どうなっていたかをイメージできる内容をフォールバック・コンテンツとして用意するのが良いでしょう。

例えば、canvas 要素にグラフを描画したいなら、そのフォールバック・コンテンツは、そのグラフの元となるデータの表(table 要素)が適切でしょう。

このように、canvas 要素に表される図画の代替となる情報を、出来る限りフォールバック・コンテンツとして用意してあげるのが重要です。

HTML

<canvas width="300" height="150">
	図形を表示するには、canvas 要素をサポートしたブラウザが必要です。
</canvas>

スクリプトによる canvas の描画

canvas 要素はマークアップしただけでは何の役にも立ちません。JavaScript を使って図を描くことで初めて意味のあるものになります。スクリプトは別途説明予定ですが、ここでは、canvas 要素の描画方法の基礎を簡単に紹介します。

ここでは、ちょっとしたデコレーションを伴った時計を作ってみましょう。このサンプルを通して、線の描画、円の描画、色の指定、テキストの描画、円形グラデーション、アニメーションなどの一通りの基礎を学ぶことができます。

このサンプルでは、左上に日付と時間が表示されますが、1秒毎に更新されます。そして、6時から 18時までは太陽が表示されます。そして 18時から 6時までは月が表示されます。太陽と月は、時間に合わせて位置が移動していきます。

夜間は、月だけでなく、星がキラキラ光るようなデコレーションを施しており、1秒毎に、表示される星の位置がランダムに変化します。

>> サンプルを表示する

サンプルの日中の表示例画像
サンプルの夜間の表示例画像

>> サンプルを表示する

では、まずHTMLをご覧頂きましょう。

HTML

<canvas id="canvas" width="300" height="150"></canvas>

この canvas 要素には、JavaScript から要素を特定しやすくするために id コンテンツ属性を指定しています。また、canvas 要素のサイズを指定するために、width コンテンツ属性と height コンテンツ属性を指定しています。

では、次に、サンプルのスクリプトを見ていきます。スクリプトの全文はサンプルページに載せています。このスクリプトは、head 要素の中の script 要素の中に直接入れても構いませんし、別ファイルとして用意して、それを script 要素の src コンテンツ属性でロードしても構いません。どちらでも動作するように作られています。

このスクリプトで使われている IDL 属性やメソッドを詳しく見ていきましょう。

canvas のコンテキスト

canvas で図形を描くためには、まず、canvas を参照したオブジェクトから、canvas コンテキストを取得しなければいけません。canvas コンテキストとは、canvas で図形を描くために必要なメソッドやプロパティがセットされたオブジェクトです。canvas コンテキストのオブジェクトは、canvas を参照したオブジェクトに組み込まれた getContext('2d') メソッドを使って取得します。

JavaScript

window.addEventListener("load", function() {
	canvas = document.getElementById("canvas");
	context = canvas.getContext('2d');
	setInterval(draw, 1000);
}, false);

getContext() メソッドには1つだけ引数を与えますが、それは '2d' で固定です。Webブラウザで3Dを扱う場合は同じ canvas 上で WebGL(getContext('webgl') など)を使います。2Dは getContext('2d') です。

これ以降は、getContext() メソッドから取り出された canvas コンテキストの context オブジェクトを通して、canvas に図形を描いていきます。

このコードでは、最後に、setInterval() メソッドを使って、1秒おきに draw 関数を呼び出すようにしています。draw 関数では、現在の日時に合わせて canvas に図形を描きます。この関数が 1秒毎に呼び出されることで、アニメーションが実現できるのです。

canvas の描画の前処理

アニメーションを実現するためには、canvas に 1コマ描く前に、canvas にすでに描かれているコマをクリアしなければいけません。クリアするためには、clearRect() メソッドを使います。クリアした後に、次のコマを描きます。

JavaScript

function draw() {
	// canvasをクリア
	context.clearRect(0, 0, 300, 150);
	// 現在の日時を取得
	var t = new Date();
	// 背景を描画
	drawBackGround(t);
	// 太陽または月を描画
	drawSunMoon(t);
	// 日時を描画
	drawDatetime(t);
}

clearRect() メソッドは、引数で指定された矩形の領域を完全にクリアします。このメソッドには、矩形の左上端の x 座標、矩形の左上端の y 座標、矩形の横幅、矩形の縦幅の順に、4つの引数を与えます。

canvas の座標は、左上端を原点 (0,0) とし、右に向かって x 座標が大きくなり、下に向かって y 座標が大きくなります。

背景の描画

このサンプルでは、昼間と夜間とで背景の色を変えます。そのためには、まず、canvas の領域全体を塗りつぶす必要があります。矩形の色の指定には、fillStyle 属性を使います。そして、矩形の塗りつぶし処理には fillRect() メソッドを使います。

JavaScript

function drawBackGround(t) {
	// 現在のcanvasの状態を保存
	context.save();
	// 背景の色を時間によって確定
	var h = t.getHours();
	if( h >= 6 && h < 18 ) {
		context.fillStyle = "#a4eaf6";
	} else {
		context.fillStyle = "#000044";
	}
	// 背景を塗りつぶす
	context.fillRect(0, 0, 300, 150);
	// canvasの状態を元に戻す
	context.restore();
}

色は描画の前に指定しなければいけません。つまり、fillStyle属性に色をセットしてから、fillRect() メソッドを呼び出す必要があります。

fillStyle 属性には、CSS で使われる色の指定方法であれば、どのフォーマットを使って指定しても構いません。ここでは、16進数表記で色を指定しています。

fillRect() メソッドには、矩形の左上端の x 座標、矩形の左上端の y 座標、矩形の横幅、矩形の縦幅の順に、4つの引数を与えます。これは、clearRect() メソッドと同じです。

なお、この関数の最初では save() メソッドが呼び出され、そして最後で restore() メソッドが呼び出されています。これは、canvas では、fillStyle 属性などでセットされた色などの情報は、この関数の処理が終わっても、そのまま引き継がれてしまうからです。このため、複雑な描画を行っていると、現在の状態が分かりにくくなります。そこで、save() メソッドを呼び出すことで、この関数の処理が実行される前の状態を保存します。そして、この関数の処理が終わった時点で、restore() メソッドを呼び出すことで、先ほど保存した状態を復帰させているのです。

このサンプルでは、canvas の状態を変更する処理が含まれる関数すべてで save() メソッドと restore() メソッドを呼び出しています。canvas のスクリプティングのテクニックの 1つとして覚えておくと便利です。

太陽の描画

太陽の描画においては、円の描画と円形グラデーションを使っています。

JavaScript

function drawSun(t) {
	// 現在のcanvasの状態を保存
	context.save();
	// 太陽を描画
	var rad = getAngle(t);
	var p = getArcArg(rad);
	context.beginPath();
	context.arc(p.x, p.y, p.radius, p.startAngle, p.endAngle, p.anticlockwise);
	var grad = context.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.radius);
	grad.addColorStop(0, "#ffffff");
	grad.addColorStop(0.5, "#FFFB13");
	grad.addColorStop(1, "#FFCB1D");
	context.fillStyle = grad;
	context.fill();
	// canvasの状態を元に戻す
	context.restore();
}

canvas では、図形を描く場合、まず最初に beginPath() メソッドを呼び出します。これは、これからパスを描き始めることを宣言するものです。パスとは、図形の輪郭を表す直線や曲線のことを表し、さまざまなメソッドを繰り返し使うことで、一筆書きをするようにパスをつなげて、図形を定義します。

先ほど使った clearRect() メソッドや fillRect() メソッドでは、事前に beginPath() メソッドを呼び出す必要はありませんが、これは canvas の中では例外と考えてください。

JavaScript

context.beginPath();

canvas では、beginPath() メソッドが呼び出されない限り、保持されているパスがクリアされません。そのため、いくつもの図形を描きたい場合には、その都度、事前に beginPath() メソッドを呼び出してください。

次に円のパスを描きます。円を描くためには、arc() メソッドを使います。このメソッドには、円の中心の x 座標、円の中心の y 座標、円の半径、円を描き始める位置の角度、円を描き終える位置の角度、円を描く方向の順番で、6つの引数を与えます。

JavaScript

context.arc(p.x, p.y, p.radius, p.startAngle, p.endAngle, p.anticlockwise);

4つ目と 5つ目の引数が表す角度の単位はラジアンです。また、6つ目の引数が表す方向には true か false を指定します。true を指定すると反時計回りに、false を指定すると時計回りに円を描画することになります。

この例では、arc() メソッドの引数を getArcArg() 関数で計算させています。

JavaScript

function getArcArg(rad) {
	var arg = {
		x: 150 + Math.cos(rad) * 100,
		y: 150 - Math.sin(rad) * 100,
		radius: 30,
		startAngle: 0,
		endAngle: Math.PI * 2,
		anticlockwise:false
	};
	return arg;
}

arc() メソッドを呼び出しただけでは、まだパスを定義しただけに過ぎず、canvas に描かれることはありません。ここでは、円を描画する前に、円に対して円形グラデーションを定義します。

円形グラデーションを実現するためには、まず createRadialGradient() メソッドを使って、グラデーション・オブジェクトを取り出します。この例では、そのオブジェクトを変数 grad にセットしています。

このメソッドには、グラデーションを開始する円を表す 3つの引数と、グラデーションを終了する円を表す 3つの引数の、合計 6つの引数を与えます。それぞれの 3つの引数は、円の中心の x 座標、円の中心の y 座標、円の半径の順番で与えます。

JavaScript

var grad = context.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.radius);

この例では、太陽の中心の点を表す円(中心が太陽と同じで半径が 0 の円)から、太陽の輪郭を表す円(中心と半径が太陽と同じ円)に向かう円形グラデーションを定義しています。

この状態では、まだ具体的な色が定義されていません。円形グラデーションの色を定義するには、オブジェクト grad に定義されている addColorStop() メソッドを使います。このメソッドには、グラデーションの位置を表す 0 から 1 までの範囲の数値、CSS で使われる色を表す文字列の順に、2つの引数を与えます。

JavaScript

grad.addColorStop(0, "#ffffff");
grad.addColorStop(0.5, "#FFFB13");
grad.addColorStop(1, "#FFCB1D");

この例では、太陽の中心位置を白に、太陽の半分の半径の位置を黄色に、太陽の外縁をオレンジに定義しています。ここでは、グラデーションのポイントを 3つ指定していますが、いくつでも指定可能です。ただし、最低 2つ指定しないとグラデーションになりませんので注意してください。

円形グラデーションの色を定義したら、fillStyle IDL 属性に、変数 grad をセットします。

JavaScript

context.fillStyle = grad;

これで、円のパス、そして円に描く円形グラデーションが定義されました。

ただし、まだ、この時点では canvas に太陽は描かれません。最後に fill() メソッドを呼び出すことで、初めて canvas に太陽が現れます。

JavaScript

context.fill();

月の描画

月の描画は、太陽と比べて簡単です。ただ単に黄色の円を描きます。そして、背景と同じ色の円を、少し位置をずらして描きます。こうすることで、月が欠けたように見えます。

JavaScript

function drawMoon(t) {
	// 現在の canvas の状態を保存
	context.save();
	// 月を描画
	var rad = getAngle(t);
	var p = getArcArg(rad);
	context.fillStyle = "yellow";
	context.beginPath();
	context.arc(p.x, p.y, p.radius, p.startAngle, p.endAngle, p.anticlockwise);
	context.fill();
	// 月の満ち欠けを描画
	var q = getArcArg(rad - Math.PI / 24);
	context.fillStyle = "#000044";
	context.beginPath();
	context.arc(q.x, q.y, q.radius, q.startAngle, q.endAngle, q.anticlockwise);
	context.fill();
	// canvas の状態を元に戻す
	context.restore();
}

円を描くために、arc() メソッドを使っていますが、これは太陽の描画と同じです。また、月はグラデーションを伴わないため、fillStyle IDL 属性に CSS カラーを表す文字列をセットしています。

星の描画

1つの星は、ただ単に、2本の黄色の線を十字に描いただけです。そして、20個の星をランダムな位置に描画しています。

JavaScript

function drawStars() {
	// 現在のcanvasの状態を保存
	context.save();
	// 星をランダムに描画
	context.strokeStyle = "rgba(255, 255, 0, 0.5)";
	for( var i=0; i<20; i++ ) {
		var s = getRandomPos();
		context.beginPath();
		context.moveTo(s.x-2, s.y);
		context.lineTo(s.x+2, s.y);
		context.stroke();
		context.beginPath();
		context.moveTo(s.x, s.y-4);
		context.lineTo(s.x, s.y+4);
		context.stroke();
	}
	// canvasの状態を元に戻す
	context.restore();
}

まず最初に strokeStyle IDL 属性を使って色をセットしています。先ほど fillStyle IDL 属性を使って円の色やグラデーションをセットしましたが、fillStyle IDL 属性は塗りつぶしを定義するものです。それに対して、ここでは線、つまりパスに対して色を指定するため、strokeStyle IDL 属性を使います。

JavaScript

context.strokeStyle = "rgba(255, 255, 0, 0.5)";

strokeStyle IDL 属性には、CSS で使う色を表す文字列であり、ブラウザが対応していれば、どんなフォーマットでも指定することが出来ます。ここでは、rgba フォーマットを使って、黄色の半透明を指定しています。次に 20回のループを使って星を描きますが、それぞれの星の描き方を見ていきましょう。

まず線を引くためには、最初にパスを描き始めることを宣言するために beginPath() メソッドを呼び出します。次に、開始点を定義するために moveTo() メソッドを呼び出します。

JavaScript

context.moveTo(s.x-2, s.y);

moveTo() メソッドには、開始点を表す x 座標と y 座標の、2つの引数を与えます。

次に、lineTo() メソッドを使って、直線を定義します。

JavaScript

context.lineTo(s.x+2, s.y);

lineTo() メソッドには、直線の終着点を表す x 座標と y 座標の、2つの引数を与えます。直線を引くために、2つの地点の座標を与えるわけではないところに注意してください。canvas でパスを描く際には、どのメソッドでも、手前の終着点を開始点と見なして処理されます。そのため、lineTo() メソッドには終着点だけを指定すれば良いのです。この例では、moveTo() メソッドで指定した座標を開始点として、lineTo() メソッドで指定した座標を終着点とする直線を表すパスが定義されたことになります。

この時点では、まだパスが定義されただけですので、canvas に直線は描かれていません。 canvas に線を描くためには、stroke() メソッドを呼び出します。

JavaScript

context.stroke();

stroke() メソッドには引数はありません。このメソッドが呼び出された時点で定義されているパスが表す輪郭を一気に描画します。

太陽を描いた時には、fill() メソッドを使いましたが、fill() メソッドは塗りつぶし用のメソッドです。つまりパスの内側を塗りつぶします。それに対して stroke() メソッドはパスが表す線を描画するためのメソッドです。これらの違いを、しっかりと理解してください。

以上で横向きの一本の線が描かれました。縦向きの線を描くために、これまでと同じ処理をもう一度行います。これで十字が完成です。

日時の描画

canvas では図形だけではなく、文字を描画することもできます。このサンプルでは日付と時間を文字として描いています。

JavaScript

function drawDatetime(t) {
	// 現在のcanvasの状態を保存
	context.save();
	// 日付の文字列
	var yer = (t.getFullYear()).toString();
	var mon = ((t.getMonth() + 1).toString()).replace(/^(\d)$/, "0$1");
	var dat = ((t.getDate()).toString()).replace(/^(\d)$/, "0$1");
	var wek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][t.getDay()];
	var date = yer + "-" + mon + "-" + dat + "(" + wek + ")";
	// 時間の文字列
	var hur = ((t.getHours()).toString()).replace(/^(\d)$/, "0$1");
	var min = ((t.getMinutes()).toString()).replace(/^(\d)$/, "0$1");
	var sec = ((t.getSeconds()).toString()).replace(/^(\d)$/, "0$1");
	var time = hur + ":" + min + ":" + sec;
	// canvasに描画
	var h = t.getHours();
	if( h >= 6 && h < 18 ) {
		context.fillStyle = "#000044";
	} else {
		context.fillStyle = "#e3f4fe";
	}
	context.font = "18px 'Monotype Corsiva'";
	context.fillText(date, 10, 30);
	context.fillText(time, 10, 50);
	// canvasの状態を元に戻す
	context.restore();
}

文字を描く前に、まず fillStyle IDL 属性を使って文字の色を指定しておきます。次に、font IDL 属性を使ってフォントを定義します。

JavaScript

context.font = "18px 'Monotype Corsiva'";

font IDL 属性には、CSS の font プロパティに指定できる文字列を指定します。

文字の色とフォントを指定したら、fillText() メソッドを呼び出して、canvas に文字を描画します。

JavaScript

context.fillText(date, 10, 30);

fillText() メソッドには、描画したい文字列、x 座標、y 座標の順に、3つの引数を与えます。指定した座標は、デフォルトでは、描画する文字列の左下端を表すことになります。左上端ではないので、注意してください。

まとめ

以上、簡単に canvas の使い方を解説してきましたが、HTML5 仕様では、もっと多くのメソッドや属性が規定されています。例えば、img 要素のイメージの組み込み、video 要素のフレームの組み込み、ベジェ曲線の描画、テキスト・シャドー描画などです。また、canvas に描かれたピクセル情報を配列データとして取り出して、そのイメージを加工することすら可能となります。

こうした canvas の詳細については、HTML5 仕様の原文か、または、HTML5.JP の Canvas チュートリアルやリファレンスをご覧ください。

よくある質問

Q. なんで canvas が真っ白?
A. getContext('2d')null の可能性。const ctx = canvas.getContext('2d'); if (!ctx) { … } で検出して案内を出す。
Q. 線がガタつく/文字がにじむ
A. 高DPIスケーリング不足。devicePixelRatio に合わせて内部解像度を拡大+ctx.scale()
Q. 画像が描けない/CORSエラー
A. 外部画像はCORS許可が必要。画像の crossOrigin="anonymous"+サーバ側ヘッダ(Access-Control-Allow-Origin)。
Q. setIntervalrequestAnimationFrame の違いは?
A. 前者は時間で実行、後者は画面の描画タイミングで実行。アニメは後者が滑らかで無駄が少ない。
Q. 3Dはこれで描ける?
A. 3Dは同じ <canvas> でもWebGL(getContext('webgl'|'webgl2'))を使う。2Dは getContext('2d')

よくあるエラー早見表

TypeError: Cannot read properties of null (reading 'getContext')
指定IDの要素が未描画 or タイポ。defer または DOMContentLoaded 後に実行。
SecurityError / CORS related when drawing Image
外部画像にCORS許可がない。サーバ側ヘッダを設定し、img.crossOrigin = "anonymous" を指定。
にじみ・ぼやける
CSSだけで拡大縮小している。width/height 属性と devicePixelRatio 対応を行う。
Canvasが重い・カクつく
毎フレームの無駄処理。見えない部分は描かない、オブジェクトの再利用、必要なら OffscreenCanvas
文字の位置が合わない
textBaseline / textAlign の既定値に注意。先に基準を決めてから fillText