JavaScript Advent Calendar 2010 4日目 canvasのいろは
Advent Calendar初参加させていただきます。
今日はHTML5の目玉のひとつであるcanvas(2D)をつかうにあたっていろはを解説したいと思います。
といいながら、canvasについてはいろいろなサイトのチュートリアルがかなり充実してます。MDC Doc Centerとか。
ここで書いていることはそのごく一部なので、興味を持った人はそういうのをみてほしいです。
3D(WebGL)の話もしたかったんですが、わたしの環境で動かないのでまたこんどにさせてください。
はじめに
書き手について
hYagni(屋国 遥)と申します。日曜プログラマで、最近はcanvasまわりあるいはFirefoxまわりのJavaScriptやPHPをよく書いてます。
最近の悩みは関数名と引数が覚えられないことです。
canvasについて
jsから簡単にいじれるピクセルベースの描画領域を提供するHTMLタグです。
詳しくはcanvas要素 - Wikipediaのリンクをみてください。
なにがうれしいか
FlashやJavaなどを使わなくても簡単にピクセル単位の画像描画ができます。一部のウェブユーザにとってFlashやJavaはプラグインが必要だったり、ブラウザと提供するユーザエクスペリエンスが違ったりと不快感があるものでした。
canvasはHTMLタグの一つですから、そういった問題が少なくなると思います。Web3Dとかもあってその実力は未知数だと思います。とりあえずわたしにとってはつかいなれたJSで画像が扱えるのが大きいです。
SVGとはどうちがうの
SVG or Canvas? Сhoosing between the two - Opera Developer Communityが端的にまとめています。
そもそもSVGはベクトルデータ、canvasはピクセルベースです。それにともなってSVGはそれぞれのオブジェクトが独立していてXMLにのっていますが、canvasは一枚の画像です。
わたしはまだSVGをさわってみてないですが、大半の(とくにわたしの)用途にはSVGのほうがよさそうで軽くへこんでます。
書く
サンプルコード: advent.zip
点を書く > 線を書く > 四角を書く くらいまでをやってみます。たんに描画しても何も面白くないので、マウスで操作するようにしましょう。
HTMLコードはindex.html、javascriptはcanvas.jsとします。
index.html
canvas elementをつくっているだけです。IDがContext取得で必要になるのでmyCanvasとしておきます。非対応ブラウザでは何かメッセージがでるようにするといいでしょう。
cssで背景やポインタをいじっています。
ここでcanvas要素の位置を(0,0)スタートにすることは非常に重要で、これをおこなわないとcanvas上の座標とclientX,Yで取得される座標がずれて、修正する必要がでます。
<canvas id="myCanvas" width="640" height="480"> Your browser doesn't support canvas element. </canvas>
canvas.js
HTMLがわのradio buttonの選択状態にあわせて3つの描画形式から選択し、その形式でおえかきします。
まず、描画関数rectとlineをつくります。
function line(fx,fy,tx,ty){ var ctx = getContext(); ctx.strokeStyle = stroke; //色設定 ctx.beginPath(); ctx.moveTo(fx,fy); ctx.lineTo(tx,ty); ctx.stroke(); } function rect(fx,fy,w,h,style){ var ctx = getContext(); ctx.fillStyle = style; ctx.fillRect(fx,fy,w,h); }
getContextはdocument.getElementById('myCanvas').getContext('2d')と同値で、これに対して操作することでcanvasのなかみをいじくります。
strokeStyleおよびfillStyleは色を決めます。今回は単色で、次のようにしました。
point = "rgb(255,100,100)"; stroke = "rgb(100,255,100)"; fill = "rgb(100,100,255)";
lineではパスを用いて線を描いています。パスの処理はbeginPathとパスを描画するstrokeかfillで挟みます(下参照)。
rectはみたまんまぬりつぶしてます。
点を打つときはrect(x-1,y-1,2,2,point)として打っています。
mousedown / mousemove / mouseupのイベントリスナでマウスの状態を取得し、start,drag,endの関数でcanvasを書き換えていきます。
mode = 0: 点の場合
どの関数でも現在位置に点をうつだけです。点が連続しないことを確認しておいてください。
mode = 1: 線の場合
prevx,prevyに前回認識した座標を入れ、いまの座標との間にlineで線を引きます。ただの線でも結構なめらかっぽいよね。
mode = 2: 四角形の場合
startの位置とendの位置を取得して、その間で四角形をかいているだけ。clearRectはどの位置から引き始めたかをわかるようにする点を消すためのものです。
これだけでも結構たのしめませんか?
保存する
書いた画像を保存したいときは、(firefoxの場合)context menuから「名前をつけて画像を保存」でOK。
やってみよう
- 今の状態では書き損じを訂正できません。clearRectをつかって消しゴムを実装してみましょう。
- ペンのサイズを変えたい時もあります。点のおおきさ、線のふとさをかえられるようにしてみましょう。
- 超頑張ればphotoshopみたいのつくれるんじゃないの?
Another Sample
こないだつくってまだ直していない人口ピラミッド生成器(pyramid.zip)でも。
こういうグラフとかはresizabilityがだいじなのでsvgでかいたほうがよさげですね。
ちょっと詳しい説明
- var ctx = document.getElementById('myCanvas').getContext('2d')
としてcontextを取得。こんごctxのメソッドをよぶことで描画処理を進めていく。 - 行列操作
- 図形の中心をずらしたい、回転させたい、拡縮したい場合は行列操作をします。しない場合はスキップしてOK。
- 行列操作とそれに伴う描画部はctx.save()とctx.restore()囲いましょう。この内部での行列への変更が外部におよばないようにです。
- 行列操作関数たち
- translate(x,y): 座標原点をx,yずらします。回転や拡大の操作の原点をうまくするために使うと思う。
- rotate(angle): 座標軸を時計回りにangle(ラジアン)だけまわします。度数法でr度反時計回りに(数学っぽく)回転して描画したかったら、rotate(-Math.PI*r/180)。
- scale(x,y): X方向にx倍、Y方向にy倍した図形を描くようにします。
- transform(m11,m12,m21,m22,dx,dy): 行列の直指定。
- 色を決める
- var style = "rgba(255,0,0,1)" のように記述します。
- ctx.strokeStyle = style or ctx.fillStyle = styleってかんじで指定します。
- 描画する
- パスを書く場合、次の順番で。
ctx.beginPath() ctx.[パスを書く命令] ctx.fill() or ctx.stroke()
fillならパスの中を塗りつぶし、strokeならパスの線だけを書きます。
パスを書く命令には円弧のarcや直線のlineToがあります - 四角形で遊ぶ場合
ctx.fillRect(x,y,w,h) ctx.strokeRect(x,y,w,h)
- 画像を表示する場合
var img = new Image(); img.src = "FilePath / data: URI" img.onload = function(){ ctx.drawImage(img,x,y); }
ctx.drawImageの引数は様々なパターンがあるのでUsing images - MDC Doc Centerを参照。
- パスを書く場合、次の順番で。
ほとんど自分用メモになってしまった・・・
これぐらいでわたしはこまってません。あとはいろいろ工夫して綺麗にみせたり、アプリケーション側を充実させてつかいやすいものにしたりしましょう。
そのほか
他にも色々指定できます。
- 線のスタイル
- lineWidth
- lineCap
- lineJoin
- miterLimit
- パターンとかグラデーションとか
- クリッピングパスや重ねあわせの属性とか
くわしくは
Canvas tutorial - MDC Doc Center
以上です。極端にまとまりがないですが、読んでいただきありがとうございます。
来年はもっといいエントリがかけるようにがんばります。
なにかありましたらコメントしていってください。