状態を保存および復元する
変換のメソッドを見ていく前に、より複雑な描画を始めたときに不可欠になメソッドを 2 つ見ておきましょう。
canvas の状態は、スタックに保存されます。save()
メソッドを呼び出すたびに、現在の描画状態をスタックにプッシュします。描画状態は以下の情報で構成されます:
- 適用された変換操作 (すなわち、後述する
translate
、rotate
、scale
)。 - 以下の属性の、現在の値:
strokeStyle
、fillStyle
、globalAlpha
、lineWidth
、lineCap
、lineJoin
、miterLimit
、lineDashOffset
、shadowOffsetX
、shadowOffsetY
、shadowBlur
、shadowColor
、globalCompositeOperation
、font
、textAlign
、textBaseline
、direction
、imageSmoothingEnabled
- 現在のクリッピングパス。これは次の章で説明します。
save()
メソッドは、何回でも呼び出すことができます。restore()
メソッドを呼び出すたびに、最後に保存された状態をスタックからポップして、すべての保存済み設定を復元します。
save
および restore
の例
この例は、連続した矩形のセットを描画するときに、描画状態のスタックがどのように機能するかを示します。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.fillRect(0,0,150,150); // 既定の設定で矩形を描画 ctx.save(); // 既定の状態を保存 ctx.fillStyle = '#09F'; // 設定変更 ctx.fillRect(15,15,120,120); // 新たな設定で矩形を描画 ctx.save(); // 現在の状態を保存 ctx.fillStyle = '#FFF'; // 設定変更 ctx.globalAlpha = 0.5; ctx.fillRect(30,30,90,90); // 新たな設定で矩形を描画 ctx.restore(); // 以前の状態を復元 ctx.fillRect(45,45,60,60); // 復元した設定で矩形を描画 ctx.restore(); // 以前の状態を復元 ctx.fillRect(60,60,30,30); // 復元した設定で矩形を描画 }
<canvas id="canvas" width="150" height="150"></canvas>
draw();
最初のステップで、大きな矩形を既定の設定で描きます。次にこの状態を保存して、塗りつぶし色を変更します。そして、2 番目のやや小さい青色の矩形を描いて、状態を保存します。もう一度描画設定を変更して、3 番目の半透明な白色の矩形を描きます。
ここまでは、これまでの章で行ってきたことによく似ています。しかし最初に restore()
文を呼び出したとき、スタックの先頭の描画状態が削除されて、その設定が復元されます。save()
を使用して状態を保存しなければ、前の状態に戻すために塗りつぶし色や透過性を手動で変更しなければなりません。ここではプロパティが 2 つであり容易ですが、プロパティが多ければコードが一気にとても長くなります。
2 番目の restore()
文を呼び出すと、元の状態 (1 番目の save
を呼び出す前に設定した状態) を復元して、最後の矩形を再び黒色で描きます。
Screenshot | Live sample |
---|---|
移動
1 番目の変換メソッドとして、translate()
を見ていきましょう。このメソッドは、canvas や canvas の原点をグリッド内の別の位置へ移動するために使用します。
translate(x, y)
- canvas や canvas の原点をグリッド上で移動します。
x
は水平方向の移動距離、y
はグリッドを垂直方向の移動距離を示します。
変換を行う前に canvas の状態を保存しておくことは、よいアイデアです。ほとんどの場合、元の状態に戻すためには逆の変換を行うよりも restore
メソッドを呼び出すほうが簡単です。また、ループ内で変換を行っているときに canvas の状態の保存や復元を行わなければ、canvas の端の外側に描画したために、描いたものの一部を失ってしまうかもしれません。
translate
の例
この例は、canvas の原点を移動する利点をいくつか示しています。translate()
メソッドを使用しなければ、すべての矩形が同じ位置 (0,0) に描かれます。また translate()
によって、fillRect()
関数で座標を手動で調整する必要なく、どこにでも自由に矩形を置くことができます。これにより若干理解しやすく、また使いやすくなります。
draw()
関数で、for
ループを使用して fillRect()
関数を 9 回呼び出しています。それぞれのループで canvas を移動して矩形を描き、その後に元の状態を復元します。描画位置を調節する translate()
を頼って、fillRect()
は毎回同じ座標を使用していることに注目してください。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); for (var i=0;i<3;i++) { for (var j=0;j<3;j++) { ctx.save(); ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)'; ctx.translate(10+j*50,10+i*50); ctx.fillRect(0,0,25,25); ctx.restore(); } } }
<canvas id="canvas" width="150" height="150"></canvas>
draw();
Screenshot | Live sample |
---|---|
回転
2 番目の変換メソッドは rotate()
です。現在の原点を中心にして canvas を回転させるために使用します。
rotate(angle)
- 現在の原点を中心にしてラジアンで示した
angle
の分、canvas を時計回りに回転します。
回転の中心は、常に canvas の原点です。中心を変更するには、translate()
メソッドを使用して canvas を移動しなければなりません。
rotate
の例
この例は、まずは canvas の原点で矩形を回転するために rotate()
メソッドを使用して、次に矩形自身の中心で回転するために translate()
の助けを借りています。
備忘: 角度はラジアン (radians) で表しており、度数 (degrees) ではありません。これは以下の方法で変換できます: radians = (Math.PI/180)*degrees
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); // 左の矩形を canvas の原点で回転する ctx.save(); // blue rect ctx.fillStyle = "#0095DD"; ctx.fillRect(30,30, 100, 100); ctx.rotate((Math.PI/180)*25); // 灰色の矩形 ctx.fillStyle = "#4D4E53"; ctx.fillRect(30,30, 100, 100); ctx.restore(); // 右の矩形を矩形の中心で回転する // draw blue rect ctx.fillStyle = "#0095DD"; ctx.fillRect(150, 30, 100, 100); ctx.translate(200, 80); // 矩形の中心に移動する // x = x + 0.5 * 幅 // y = y + 0.5 * 高さ ctx.rotate((Math.PI/180)*25); // 回転する ctx.translate(-200, -80); // 元の位置に移動する // 灰色の矩形を描く ctx.fillStyle = "#4D4E53"; ctx.fillRect(150, 30, 100, 100); }
矩形を中心で回転するために、canvas を矩形の中心へ移動した後に canvas を回転します。そして canvas を 0,0 へ移動した後に矩形を描きます。
<canvas id="canvas" width="300" height="200"></canvas>
draw();
Screenshot | Live sample |
---|---|
スケーリング
次の変換メソッドはスケーリングです。canvas のグリッドの単位を増減するために使用します。これは、図形やビットマップを縮小または拡大して描くために使用できます。
scale(x, y)
- canvas の単位を x (水平方向) または y (垂直方向) で指定した分スケーリングします。どちらの引数も実数です。1.0 より小さい値は単位あたりのサイズが減少、1.0 より大きい値は単位あたりのサイズが増加します。1.0 では単位あたりのサイズが変わりません。
負数を使用すると軸を反転できます (例えば translate(0,canvas.height); scale(1,-1);
で、原点が左下の隅にある有名なデカルト座標系になります)。
デフォルトでは、canvas の 1 単位は 1 ピクセルとまったく同じです。例えば、スケーリング係数に 0.5 を適用すると 1 単位が 0.5 ピクセルになり、図形が半分のサイズで描かれます。同様にスケーリング係数を 2.0 に設定すると単位あたりのサイズが増えて、1 単位あたり 2 ピクセルになります。この結果、図形は 2 倍の大きさで描かれます。
scale
の例
この例は、図形をさまざまなスケーリング係数で描きます。
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); // シンプルな図形を描いて、スケーリングする ctx.save(); ctx.scale(10, 3); ctx.fillRect(1,10,10,10); ctx.restore(); // 水平方向に反転する ctx.scale(-1, 1); ctx.font = "48px serif"; ctx.fillText("MDN", -135, 120); }
<canvas id="canvas" width="150" height="150"></canvas>
draw();
Screenshot | Live sample |
---|---|
変形
最後に、以下の変換メソッドで、変換行列によって直接変更することができます。
transform(a, b, c, d, e, f)
- 引数で表した行列と、現在の変換行列で乗算を行います。変換行列は以下のとおりです:
- いずれかのひきうすが
Infinity
になる場合は、メソッドで例外を発生させるのではなく行列を infinite としてマークしなければなりません。
この関数の引数は以下のとおりです:
a (m11)
- 水平方向のスケーリング。
b (m12)
- 水平方向のスキュー。
c (m21)
- 垂直方向のスキュー。
d (m22)
- 垂直方向のスケーリング。
e (dx)
- 水平方向の移動。
f (dy)
- 垂直方向の移動。
setTransform(a, b, c, d, e, f)
- 現在の変形を単位行列にリセットして、同じ引数で
transform()
メソッドを呼び出します。これは基本的に、現在の変形をアンドゥしてから指定した変形を行う操作を一度に行うものです。 resetTransform()
- 現在の変形を単位行列にリセットします。これは
ctx.setTransform(1, 0, 0, 1, 0, 0);
を呼び出すことと同じです。
transform
と setTransform
の例
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var sin = Math.sin(Math.PI/6); var cos = Math.cos(Math.PI/6); ctx.translate(100, 100); var c = 0; for (var i=0; i <= 12; i++) { c = Math.floor(255 / 12 * i); ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")"; ctx.fillRect(0, 0, 100, 10); ctx.transform(cos, sin, -sin, cos, 0, 0); } ctx.setTransform(-1, 0, 0, 1, 100, 100); ctx.fillStyle = "rgba(255, 128, 255, 0.5)"; ctx.fillRect(0, 50, 100, 100); }
<canvas id="canvas" width="200" height="250"></canvas>
draw();
Screenshot | Live sample |
---|---|