ホームページ  >  記事  >  ウェブフロントエンド  >  Canvas ゲーム開発学習パート 7: 変換

Canvas ゲーム開発学習パート 7: 変換

黄舟
黄舟オリジナル
2017-01-17 09:43:371427ブラウズ

状態の保存と復元

変形を理解する前に、まず、複雑なグラフィックを描き始めるときに不可欠な 2 つの方法を紹介します。

save()
restore()

メソッドとrestore メソッドは、キャンバスの状態を保存および復元するために使用され、両方ともパラメーターがありません。キャンバスの状態は、現在の画面に適用されているすべてのスタイルと変換のスナップショットです。 Canvas の状態はスタックに保存され、save メソッドが呼び出されるたびに、現在の状態がスタックにプッシュされて保存されます。 save メソッドは何度でも呼び出すことができます。復元メソッドが呼び出されるたびに、最後に保存された状態がヒープからポップされ、すべての設定が復元されます。

保存と復元の応用例

この連続長方形の例を使用して、キャンバス状態ヒープがどのように機能するかを説明してみます。

Canvas ゲーム開発学習パート 7: 変換

最初のステップは、デフォルト設定で大きな正方形を描画し、状態を保存することです。塗りつぶしの色を変更し、2 番目の小さな青い四角形を描画し、状態を再度保存します。塗りつぶしの色を再度変更し、小さな半透明の白い四角形を描画します。これまでに実行したアクションは、前の章のアクションと非常によく似ています。ただし、restore を呼び出すと、状態ヒープ内の最後の状態がポップアップ表示され、すべての設定が復元されます。以前に save で状態を保存していない場合は、設定を前の状態に戻す必要があります。属性が増えると、コードは大幅に増加します。 2回目にrestoreを呼び出すと元の状態に戻っているので、最終的にはまた黒い四角が描画されます。

function draw() {  
     var ctx = document.getElementById('canvas').getContext('2d');  
      
     ctx.fillRect(0,0,150,150);   // Draw a rectangle with default settings  
     ctx.save();                  // Save the default state  
      
     ctx.fillStyle = '#09F'       // Make changes to the settings  
     ctx.fillRect(15,15,120,120); // Draw a rectangle with new settings  
      
     ctx.save();                  // Save the current state  
     ctx.fillStyle = '#FFF'       // Make changes to the settings  
     ctx.globalAlpha = 0.5;      
     ctx.fillRect(30,30,90,90);   // Draw a rectangle with new settings  
     
     ctx.restore();               // Restore previous state  
     ctx.fillRect(45,45,60,60);   // Draw a rectangle with restored settings  
     
     ctx.restore();               // Restore original state  
     ctx.fillRect(60,60,30,30);   // Draw a rectangle with restored settings  
 }

移動移動

まず、キャンバスとその原点を別の位置に移動するために使用されるtranslateメソッドを紹介します。

translate(x, y)
translate

メソッドは 2 つのパラメータを受け入れます。以下の図に示すように、x は左右のオフセット、y は上下のオフセットです。

Canvas ゲーム開発学習パート 7: 変換

変形を行う前に状態を保存することは良い習慣です。ほとんどの場合、手動で元の状態を復元するよりも、restore メソッドを呼び出す方がはるかに簡単です。また、ループ内でディスプレイスメントを実行するが、キャンバスの状態を保存および復元しない場合は、キャンバスの範囲を超えている可能性が高いため、最終的に何かが欠けていることに気づくでしょう。

翻訳

この例は、キャンバスの原点を移動する利点のいくつかを示しています。原点の周りに描かれるスパイラル (スピログラフ) パターンを描画するための、drawSpir​​ograph メソッドを作成しました。翻訳メソッドを使用しない場合は、全体の 4 分の 1 だけが表示されます。また、Translate を使用すると、スピログラフ法で座標値を手動で調整する必要がなく、これらのパターンを任意に配置することができ、理解しやすく、使いやすくなります。 2 層のループを使用して、draw メソッド内でdrawSpir​​ograph メソッドを 9 回呼び出しました。ループを通過するたびに、キャンバスが最初に移動され、螺旋パターンが描かれ、その後元の状態に戻ります。

Canvas ゲーム開発学習パート 7: 変換

function draw() {  
      var ctx = document.getElementById('canvas').getContext('2d');  
      ctx.fillRect(0,0,300,300);  
      for (var i=0;i<3;i++) {  
        for (var j=0;j<3;j++) {  
          ctx.save();  
          ctx.strokeStyle = "#9CFF00";  
          ctx.translate(50+j*100,50+i*100);  
          drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10);  
         ctx.restore();  
        }  
      }  
   }  
  function drawSpirograph(ctx,R,r,O){  
     var x1 = R-O;  
     var y1 = 0;  
     var i  = 1;  
     ctx.beginPath();  
     ctx.moveTo(x1,y1);  
     do {  
       if (i>20000) break;  
       var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))  
       var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))  
       ctx.lineTo(x2,y2);  
       x1 = x2;  
       y1 = y2;  
       i++;  
     } while (x2 != R-O && y2 != 0 );  
     ctx.stroke();  
  }

Rotating Rotating

2 つ目は、原点を中心にしてキャンバスを回転するために使用される、rotate
メソッドを紹介します。

rotate(angle)

这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到translate
方法。

Canvas ゲーム開発学習パート 7: 変換


rotate
的例子

在这个例子里,见下图,我用rotate方法来画圆并构成圆形图案。当然你也可以分别计算出 x 和 y 坐标(x = r*Math.cos(a); y = r*Math.sin(a))。这里无论用什么方法都无所谓的,因为我们画的是圆。计算坐标的结果只是旋转圆心位置,而不是圆本身。即使用rotate旋转两者,那些圆看上去还是一样的,不管它们绕中心旋转有多远。这里我们又用到了两层循环。第一层循环决定环的数量,第二层循环决定每环有多少个点。每环开始之前,我都保存一下 canvas 的状态,这样恢复起来方便。每次画圆点,我都以一定夹角来旋转 canvas,而这个夹角则是由环上的圆点数目的决定的。最里层的环有 6 个圆点,这样,每次旋转的夹角就是 360/6 = 60 度。往外每一环的圆点数目是里面一环的 2 倍,那么每次旋转的夹角随之减半。

Canvas ゲーム開発学習パート 7: 変換

 function draw() {  
      var ctx = document.getElementById(&#39;canvas&#39;).getContext(&#39;2d&#39;);  
      ctx.translate(75,75);  
      
      for (var i=1;i<6;i++){ // Loop through rings (from inside to out)  
        ctx.save();  
        ctx.fillStyle = &#39;rgb(&#39;+(51*i)+&#39;,&#39;+(255-51*i)+&#39;,255)&#39;;  
      
        for (var j=0;j<i*6;j++){ // drawindividual dots  
         ctx.rotate(Math.PI*2/(i*6));  
         ctx.beginPath();  
         ctx.arc(0,i*12.5,5,0,Math.PI*2,true);  
         ctx.fill();  
       }       
       ctx.restore();  
     }  
  }

缩放 Scaling

接着是缩放。我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。

scale(x, y)

scale
方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。默认情况下,canvas 的 1 单位就是 1 个像素。举例说,如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。

scale
的例子

Canvas ゲーム開発学習パート 7: 変換

这最后的例子里,我再次启用前面曾经用过的 spirograph 方法,来画 9 个图形,分别赋予不同的缩放因子。左上角的图形是未经缩放的。黄色图案从左到右应用了统一的缩放因子(x 和 y 参数值是一致的)。看下面的代码,你可以发现,我在画第二第三个图案时scale了两次,中间没有 restorecanvas 的状态,因此第三个图案的缩放因子其实是 0.75 × 0.75 = 0.5625。第二行蓝色图案堆垂直方向应用了不统一的缩放因子,每个图形 x 方向上的缩放因子都是 1.0,意味着不缩放,而 y 方向缩放因子是 0.75,得出来的结果是,图案被依次压扁了。原来的圆形图案变成了椭圆,如果细心观察,还可以发现在垂直方向上的线宽也减少了。第三行的绿色图案与第二行类似,只是缩放限定在横轴方向上了。

function draw() {
  var ctx = document.getElementById(&#39;canvas&#39;).getContext(&#39;2d&#39;);
  ctx.strokeStyle = "#fc0";
  ctx.lineWidth = 1.5;
  ctx.fillRect(0,0,300,300);

  // Uniform scaling
  ctx.save()
  ctx.translate(50,50);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(0.75,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(133.333,0);
  ctx.scale(0.75,0.75);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();

  // Non uniform scaling (y direction)
  ctx.strokeStyle = "#0cf";
  ctx.save()
  ctx.translate(50,150);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();

  // Non uniform scaling (x direction)
  ctx.strokeStyle = "#cf0";
  ctx.save()
  ctx.translate(50,250);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(133.333,0);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(177.777,0);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();
  
}
function drawSpirograph(ctx,R,r,O){
  var x1 = R-O;
  var y1 = 0;
  var i  = 1;
  ctx.beginPath();
  ctx.moveTo(x1,y1);
  do {
    if (i>20000) break;
    var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
    var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
    ctx.lineTo(x2,y2);
    x1 = x2;
    y1 = y2;
    i++;
  } while (x2 != R-O && y2 != 0 );
  ctx.stroke();
}

变形 Transforms

最后一个方法是允许直接对变形矩阵作修改。

m11     m21     dx
m12     m22     dy
0       0       1
setTransform(m11, m12, m21, m22, dx, dy)

这个方法必须重置当前的变形矩阵为单位矩阵,然后以相同的参数调用transform
方法。如果任意一个参数是无限大,那么变形矩阵也必须被标记为无限大,否则会抛出异常。

transform/setTransform的例子

Canvas ゲーム開発学習パート 7: 変換

function draw() {  
      var canvas = document.getElementById("canvas");  
      var ctx = canvas.getContext("2d");  
      
      var sin = Math.sin(Math.PI/6);  
      var cos = Math.cos(Math.PI/6);  
      ctx.translate(200, 200);  
      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, 200, 200);  
     ctx.fillStyle = "rgba(255, 128, 255, 0.5)";  
     ctx.fillRect(0, 50, 100, 100);  
   }

以上就是canvas游戏开发学习之七:变形的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。