HTML5のcanvasを使ったお絵かきツール詳説
HTML5のcanvasを使ったお絵かきツールを勉強がてら作ってみました。下記の画像リンクをクリックすればサンプルページにジャンプします。
以下、サンプルのHTMLとJavaScript(jQuery)を使って、canvasのお絵かきツールについて解説します。
探しきれてないだけと思いますが、お絵かきツールのjQueryでの実装が見つからなかったので、自力で書いてみました。いろいろ間違ってたらすいません。
1.HTML
サンプルのHTMLは次のようになっています。
<canvas width="500" height="400">お使いのブラウザはHTML5のCanvas要素に対応していません。</canvas>
<ul>
<li style="background-color:#000"></li>
<li style="background-color:#f00"></li>
<li style="background-color:#0f0"></li>
<li style="background-color:#00f"></li>
<li style="background-color:#ff0"></li>
</ul>
<div id="button">
<input type="button" id="save" value="画像で保存" />
<input type="button" id="clear" value="消去" />
</div>
1行目のcanvas要素はブラウザ上に図を描くためのものです。width属性・height属性でcanvas要素のサイズを設定します。
canvas要素はフォールバック(問題発生時の代替手段)も考慮されており、HTML5のcanvas要素に対応していないブラウザなどでは要素のコンテンツを出力します。
そのあとにあるli要素は、Canvas上で色を変更するためのものです。また、input要素でcanvasに描画された内容の消去と保存を行えるようにしています。
2.JavaScript
とりあえず全体のコードです。jQueryで書いてますがjQueryの解説は割愛しますので、分からない方は他のサイトや書籍等で調べてください。
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
var offset = 5;
var startX;
var startY;
var flag = false;
var canvas = $('canvas').get(0);
if (canvas.getContext) {
var context = canvas.getContext('2d');
}
$('canvas').mousedown(function(e) {
flag = true;
startX = e.pageX - $(this).offset().left - offset;
startY = e.pageY - $(this).offset().top - offset;
return false; // for chrome
});
$('canvas').mousemove(function(e) {
if (flag) {
var endX = e.pageX - $('canvas').offset().left - offset;
var endY = e.pageY - $('canvas').offset().top - offset;
context.lineWidth = 2;
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.stroke();
context.closePath();
startX = endX;
startY = endY;
}
});
$('canvas').on('mouseup', function() {
flag = false;
});
$('canvas').on('mouseleave', function() {
flag = false;
});
$('li').click(function() {
context.strokeStyle = $(this).css('background-color');
});
$('#clear').click(function(e) {
e.preventDefault();
context.clearRect(0, 0, $('canvas').width(), $('canvas').height());
});
$('#save').click(function() {
var d = canvas.toDataURL('image/png');
d = d.replace('image/png', 'image/octet-stream');
window.open(d, 'save');
});
});
</script>
変数定義・初期化
最初に変数の定義と初期化を行います。
var offset = 5;
var startX;
var startY;
var flag = false;
offsetは、描画するときに描画線がマウスカーソルの左上になるようにするための補正値です。startX/startYは描画の開始位置、flagは描画要否を示すフラグです。
描画コンテキストの取得
「HTML5のcanvas要素の描画コンテキストをgetElementById()の代わりにjQueryで取得する」でも紹介しましたが、以下の部分で「2D描画コンテキスト」を取得します。
var canvas = $('canvas').get(0);
if (canvas.getContext) {
var context = canvas.getContext('2d');
}
この部分はcanvasを利用するときのおまじないと思っておけばよいでしょう。
mousedownイベントでの動作
mousedownイベント(マウスボタンをクリック)で次の処理を行います。
$('canvas').mousedown(function(e) {
flag = true;
startX = e.pageX - $(this).offset().left - offset;
startY = e.pageY - $(this).offset().top - offset;
return false; // for chrome
});
flagに「true」を設定します。この値はマウスを動かしたときに利用します。startXにマウスをクリックしたカーソル位置のX座標、startYにY座標を設定します。
「pageX」は、ブラウザの左端からの位置(もっと厳格な定義があるかもしれません)、「offset().left」はブラウザの左端からcanvas要素の左端のオフセットです。例えば、「pageX」が200、「offset().left」が20であれば、X座標のカーソル位置は180になり、そこからさらに補正値のoffsetを減算します。
「return false;」は、Google chromeでcanvas上でmousedownした後カーソルが「cursor:text」の形状になってしまうので、それを回避するための設定です。
mousemoveイベントでの動作
mousemoveイベント(マウスをクリックした状態で移動)で次の処理を行います。
$('canvas').mousemove(function(e) {
if (flag) {
var endX = e.pageX - $('canvas').offset().left - offset;
var endY = e.pageY - $('canvas').offset().top - offset;
context.lineWidth = 2;
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.stroke();
startX = endX;
startY = endY;
}
});
mousedownイベントで設定したflagを判定して、trueの場合のみ処理を続行します。これは、たとえばcanvasの外側からマウスをクリックした状態でcanvasに進入してきた場合に描画動作を行わないようにするためです。
endX/endYはさきほどのstartX/startYと同じです、マウスをクリックした位置よりも僅かでもマウスが動くとこのmousemoveが発動して、endX/endYを取得します。
次に、描画コンテキストに値を設定します。lineWidthは描画線の太さ、beginPath()は現在のパスをリセットします。
moveTo()で新しいサブパスの開始点を座標指定し、lineTo()で直前の座標と指定座標を結ぶ直線を引きます。stroke()は現在の線のスタイルでサブパスを輪郭表示するためのものです。さきほど設定したlineWidthがスタイルに該当します。
描画を終えたら次のmousemoveイベントに備えて、endX/endYの値をstartX/startYに設定します。
これで大体お分かりと思いますが、お絵かきツールは短い線の描画の集合ということです。
mousemoveイベント・mouseleaveイベントでの動作
mouseupイベント(マウスのクリックをやめたとき)またはmouseleaveイベント(マウスがcanvas要素からはずれたとき)で次の処理を行います。
$('canvas').on('mouseup', function() {
flag = false;
});
$('canvas').on('mouseleave', function() {
flag = false;
});
flagに「false」を設定して、その状態でcanvas要素上でマウスを動かしても描画されないようにします。
描画色の変更
次のコードで描画色の変更をできるようにします。
$('li').click(function() {
context.strokeStyle = $(this).css('background-color');
});
li要素で用意した背景色の部分をクリックすることで、その色を描画コンテキストのstrokeStyleに設定します。strokeStyleは線や輪郭の色などを指定するためのものです。
canvasのクリア
id属性「clear」のinput要素のclickイベントを契機にcanvasのクリアを行います。
$('#clear').click(function(e) {
e.preventDefault();
context.clearRect(0, 0, $('canvas').width(), $('canvas').height());
});
preventDefaultはイベントのキャンセルを通知するメソッドです。clearRect()は、引数で指定したcanvas上の矩形のすべてのピクセルを背景色でクリアします。
canvasの描画を画像で保存
canvasの内容は画像で保存できるようです。id属性「save」のinput要素のclickイベントを契機に画像の保存を行います。
$('#save').click(function() {
var d = canvas.toDataURL('image/png');
d = d.replace('image/png', 'image/octet-stream');
window.open(d, 'save');
});
toDataURL()は、canvasのイメージに対するdata:URLを返却(=ピクセルデータをBase64形式に変換)します。replace()でMIME形式の「image/png」を「image/octet-stream」に変換して、window.open()することでファイルとして出力できます。
保存したあとは、ファイル名に拡張子「.png」を付与してください。
3.参考サイト
参考サイトは下記です。ありがとうございました。
HTML5のcanvas要素の描画コンテキストをgetElementById()の代わりにjQueryで取得する
HTML5のcanvas要素の描画コンテキストを、getElementById()の代わりにjQueryで取得する方法です。jQueryな方はタイトルだけで記事の主旨がお分かりと思いますので読み飛ばしてください(笑)。
1.canvas要素の描画コンテキストとは
前置きになりますが、canvas要素は描画機能にアクセスするためのgetContext()というDOMメソッドを持っています。canvas要素のDOMノードを取り出し、取り出したDOMノードに対してgetContext('2d')を実行することで、描画機能にアクセス可能な「描画コンテキスト」を取得します。
getContext()の引数の「2d」は、描画コンテキストの種類を示し、現在「2d」固定です。要するに「2d」を指定することで「2D描画コンテキスト」を取得します。将来的に、例えば「3d」という引数が増えて3D描画コンテキストを取得できるようになるかもしれません。
2.getElementById()が使われる箇所
ネットで色々調べたところ、描画コンテキスト取得のサンプルは概ね次のようになっていました。以下はMDNからの引用です。
<script>
var canvas = document.getElementById('tutorial');
if (canvas.getContext){ // 未サポートブラウザでの実行を抑止
var ctx = canvas.getContext('2d');
// 描画用のコードを記述
}
</script>
<canvas id="tutorial" width="150" height="150"></canvas>
ポイントは、getContext()を実行するためのDOM要素「canvas」を取得するためにgetElementById()を実行している赤色部分で、単純にこの部分をjQueryで表現できないかと思った次第です。
3.jQueryを適用する
getElementById()の代わりにjQueryを適用するには、次のように記述します。
<script>
var canvas = $('#tutorial').get(0);
if (canvas.getContext){ // 未サポートブラウザでの実行を抑止
var ctx = canvas.getContext('2d');
// 描画用のコードを記述
}
</script>
<canvas id="tutorial" width="150" height="150"></canvas>
または、
<script>
var canvas = $('#tutorial')[0];
if (canvas.getContext){ // 未サポートブラウザでの実行を抑止
var ctx = canvas.getContext('2d');
// 描画用のコードを記述
}
</script>
<canvas id="tutorial" width="150" height="150"></canvas>
このように指定する理由は、$()で返却される値が配列になっているためです。id指定で返却値が1つと分かっている場合でも配列で返却されます。
jQueryオブジェクトとして扱う場合は配列を意識することはあまりないと思いますが、ここではDOM要素として取得するために配列を意識した処理を行っています。
ちなみに、未サポートブラウザのチェックが不要であれば次のように書けます。
<script>
var context = $('#tutorial').get(0).getContext('2d');
// 描画用のコードを記述
}
</script>
<canvas id="tutorial" width="150" height="150"></canvas>
以上です。認識誤り等ありましたらご指摘ください。