Canvasに入門してみた。

最近Canvasと触れ合う機会があり、色々勘違いしていたり、こんなことも出来るんだみたいなことがあったのでメモ。
描画をプログラミングでするのがはじめてだったので、やっている人には常識なことばかりかもしれませんが。

参考

Canvasの基準点

  • Canvasには基準点というのがあって、全てはその位置から「x」にいくら「y」にいくらというように図形を配置する場所を指定します。例えばx軸に「100」、y軸に「50」ずらした「40」の正方形を描画するには下記のように指定します。(他にも指定する方法はあります)
// ctxはcanvasのcontext
var ctx = document.getElementById('canvas_sample').getContext('2d');
ctx.fillRect(100, 50, 40, 40);
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = "red";
ctx.fillRect(100, 50, 100, 50);
    • この例だと黒は「(0, 0)」の基準点からで赤は「(100, 50)」ずらして四角を書いています。
rotateも基準点を中心に行われる
  • 例えば上記の正方形を45度回転させたいなぁと思ったときに、上記fillRect指定プラスrotateをすると、「(0,0)」を起点として45度回転するので当然移動しながら傾くことになります。
  • http://jsdo.it/koba04/canvas_rotate_1
ctx.fillRect(50, 0, 60, 60);
ctx.rotate(45 * Math.PI / 180);
ctx.fillStyle = "red";
ctx.fillRect(50, 0, 60, 60);
    • こんな感じで左上の「(0,0)」を中心に45度回転してしまいます。
translate大事
  • えっ、もしかして図形単位で傾けたり出来ないの??とか思っちゃったのですが、そんなことはなくてtranslateを使えば出来ます。
  • translateを使うことで最初は「(0,0)」にある基準点を移動させることが出来ます。
  • なので、図形をだけをその位置で45度回転させたいときは、translateで基準点を図形を置きたい位置まで持っていってrotateで回転させ、fillRect(0,0,60,60)のように「(x,y)」を「(0,0)」で図形を作ります。
  • http://jsdo.it/koba04/canvas_rotate_2
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.fillRect(50, 50, 60, 60);

// translatedeで基準点をズラす。
ctx.translate(50, 50);

ctx.fillStyle = "rgba(0, 255, 0, 0.5)";
ctx.rotate(45 * Math.PI / 180);
// ここでは0, 0で指定する(translateで移動しているので)
ctx.fillRect(0, 0, 60, 60);
    • この場合、図形の左上を基準に45度回転していることがわかります。先ほどとの違いとしては、translateで「(50,50)」に移動しているので、fillRect(0, 0, 60, 60)とtranslateで移動した地点「(50, 50)」からの座標を指定しています。
  • そうではなく、四角の中心で回転したいときは、四角の中心までtranslateしてそこを中心に四角を描いてrotateします。
  • http://jsdo.it/koba04/canvas_rotate_3
ctx.fillStyle = "rgba(0, 255, 0, 0.5)";
ctx.translate(50,50);
ctx.fillRect(0, 0, 40, 40);
ctx.translate(20, 20);
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";

ctx.rotate(45 * Math.PI / 180);
ctx.fillRect(-20, -20, 40, 40);
    • ここでは元の四角の中心まで移動した後、四角を「(width / 2 * -1, height / 2 * -1)」 (基準点は中心点からマイナスになるので)の地点から描いて回転させます。

resoreとsaveで局所化

  • 上記のようなtranslateやrotate、各種Styleはその後の描画の全部にかかってきますので、一部の画像だけを反転させたり色を変えたりする場合は、設定を変更し図形を描画した後、以前の状態を設定する必要が出てきます。
  • そのような場合に、save()、restore()を使うと設定の保存と復元をスタックモデルでやってくれます。
  • 具体的には、現在の状態をsave()で保存し、特別の設定で描画し、終了したらrestore()で元に戻します。
  • なんか特定の座標に対してtranslateしたり、Styleを指定するときはとりあえず指定する前にsave()して終わったらrestore()しておくと安全かもしれません。
:
// 保存しておく
ctx.save();
ctx.fiiStyle = "rgba(255,255,255,0.5)";
ctx.translate( 10, 10);
:
// 戻す
ctx.restore();
:

scaleで画像を反転させる

  • scaleを使うと図形を変形させることが可能になるのですが、これを利用して画像を反転させたりすることが出来ます。
  • http://jsdo.it/koba04/scale_img
var image = new Image();
image.src = "http://www.google.co.jp/intl/en_com/images/srpr/logo1w.png";
image.onload =function(){
  ctx.drawImage(image, 0, 0);
  ctx.scale(-1,1);
  ctx.translate(0, 150);
  ctx.drawImage(image, -this.width,0);
};
    • ここでは、scale(-1, 1)とすることで左右を反転させています。反転していない画像と同じ場所に指定するには ctx.drawImage(image, -this.width,0);と「画像の横幅 * -1」の値を指定する必要があります。
      • scale(-1, 1)することで、画像は基準点からマイナスの方向に描画されるようになるため。
  • ただ、これはAndroid 2.0/2.1 ではCanvas実装のバグがあるためうまく動きません。それについてはまた別途書きたいと思っています。

画像に文字を書く。

  • fillTextメソッドを使うことで文字を書くことも出来ます。ただ、ボックスの中に収まるようにするにはちょっと工夫する必要があります。
  • http://jsdo.it/koba04/canvas_filltext
var text = "描画する幅によって改行する位置を決めているサンプル";
var column = [""];
var line = 0;
for (var i=0; i < text.length; ++i) {
  var char = text.charAt(i);
  if ( ctx.measureText(column[line] + char).width > 100 ) {
    ++line;
    column[line] = "";
  }
    column[line] += char;
}
ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
ctx.fillRect(0, 60, 100, 50);
ctx.fillStyle = "rgba(0, 0, 0, 1)";

for (var j=0; j < column.length; ++j) {
  ctx.fillText(column[j], 0, 75 + 15 * j);  
}


// divに書いてのせる
ctx.fillStyle = "rgba(0, 255, 255, 0.5)";
ctx.fillRect(0, 150, 100, 100);
var div = document.createElement('div');
div.appendChild(document.createTextNode("これはdivの中に書いている"));
div.style.fontSize = "13px";
div.style.width = "100px";
div.style.position = "absolute";
div.style.top = "155px";
div.style.left = "10px";
document.getElementById('parent').appendChild(div);


// 画像の上に文字を書く
var image = new Image();
image.src = "http://www.google.co.jp/intl/en_com/images/srpr/logo1w.png";
image.onload =function(){
  ctx.drawImage(image, 0, 250);
  ctx.fillStyle = "rgb(0, 0, 0)";
  ctx.font = "25px 'sans-serif'";
  ctx.fillText("グーグル", 0, 340);
};
    • オプション指定の第3引数のMaxWidthを指定するとその幅に合うように文字列を伸縮してくれるらしいのですが、今やってみると何故かうまく動いてくれないのと、フォントを小さくされてもいいから枠の中に入れたいっていう状況もあまりないかなということで、「ctx.measureText("文字列").width」で1文字ずつ追加しながら長さを取得して、幅を越えたら改行するという方法があります。正直面倒なので、divとかに文字列書いてcanvasに重ねるのが楽だと思います。
  • これを利用すると、「写真でひとこと」みたいな大喜利のサービスを作ることも出来ますね。ユーザーのひとことをCanvasで写真と合成して表示するみたいな。写真を用意するのが一番大変だと思いますが。

感想

  • Canvasを使う場合でも、全てをCanvasで行わなくてもCSS3で画像の回転、拡大、縮小などは出来るので、それらを適宜組み合わせることを考えるのがいいのかなと思います。どちらの場合にしても、どのOS(Win, Mac, iOS, Android)でどのバージョン、どのブラウザをサポートするかによって使用できる技術が変わってくるので、対象をしっかり決めておくのが大切だと思いました。