캔버스 게임 개발 학습 7부: 혁신

黄舟
풀어 주다: 2017-01-17 09:43:37
원래의
1430명이 탐색했습니다.

상태 저장 및 복원

변형을 이해하기에 앞서, 복잡한 그래픽을 그리기 시작할 때 꼭 필요한 두 가지 방법을 먼저 소개하겠습니다.

save()
restore()
로그인 후 복사

및 복원 메소드는 캔버스 상태를 저장하고 복원하는 데 사용되며 매개변수가 없습니다. Canvas의 상태는 현재 화면에 적용된 모든 스타일과 변환의 스냅샷입니다. Canvas 상태는 save 메서드가 호출될 때마다 스택에 저장됩니다. 원하는 만큼 save 메소드를 호출할 수 있습니다. 복원 메서드가 호출될 때마다 마지막으로 저장된 상태가 힙에서 팝되고 모든 설정이 복원됩니다.

저장 및 복원의 적용 예

우리는 연속 직사각형의 예를 사용하여 캔버스 상태 힙이 작동하는 방식을 설명하려고 합니다.

캔버스 게임 개발 학습 7부: 혁신

첫 번째 단계는 기본 설정으로 큰 사각형을 그린 다음 상태를 저장하는 것입니다. 채우기 색상을 변경하고 두 번째로 작은 파란색 정사각형을 그린 다음 상태를 다시 저장합니다. 채우기 색상을 다시 변경하고 더 작은 반투명 흰색 사각형을 그립니다. 지금까지 수행된 작업은 이전 장의 작업과 매우 유사합니다. 그러나 일단 복원을 호출하면 상태 힙의 마지막 상태가 팝업되고 모든 설정이 복원됩니다. 이전에 저장으로 상태를 저장하지 않은 경우 설정을 수동으로 이전 상태로 다시 변경해야 합니다. 이는 여전히 2~3개의 속성에 적용 가능하며, 코드가 급증합니다. 두 번째로 복원을 호출하면 원래 상태로 복원되었기 때문에 결국에는 다시 검은색 사각형이 그려지게 됩니다.

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  
 }
로그인 후 복사

Move Translating

먼저 캔버스와 원점을 다른 위치로 이동하는 번역 방법을 소개합니다.

translate(x, y)
translate
로그인 후 복사

메소드는 두 개의 매개변수를 허용합니다. x는 아래 그림과 같이 왼쪽 및 오른쪽 오프셋이고 y는 위쪽 및 아래쪽 오프셋입니다.

캔버스 게임 개발 학습 7부: 혁신

변환을 수행하기 전에 상태를 저장하는 것이 좋습니다. 대부분의 경우 복원 메서드를 호출하는 것이 원래 상태를 수동으로 복원하는 것보다 훨씬 간단합니다. 또한 루프에서 변위를 수행했지만 캔버스의 상태를 저장하고 복원하지 않으면 결국 캔버스의 범위를 벗어날 가능성이 높기 때문에 결국 뭔가 누락되었음을 발견하게 될 것입니다.

번역

이 예는 캔버스 원점 이동의 이점 중 일부를 보여줍니다. 원점 주위에 그려지는 나선형(spirograph) 패턴을 그리는 drawSpirograph 메서드를 만들었습니다. 번역 방법을 사용하지 않으면 1/4만 표시됩니다. 또한 Translate를 사용하면 스피로그래프 방식으로 좌표 값을 수동으로 조정할 필요 없이 이러한 패턴을 임의로 배치할 수 있어 이해하고 사용하기 쉽습니다. 저는 2개의 루프 레이어를 사용하여 draw 메소드에서 drawSpirograph 메소드를 9번 호출했습니다. 루프를 통과할 때마다 먼저 캔버스를 이동하고 나선형 패턴을 그린 다음 원래 상태로 복원합니다.

캔버스 게임 개발 학습 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

두 번째는 원점을 중심으로 캔버스를 회전시키는 회전
방법을 소개합니다.

rotate(angle)
로그인 후 복사

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

캔버스 게임 개발 학습 7부: 혁신


rotate
的例子

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

캔버스 게임 개발 학습 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
的例子

캔버스 게임 개발 학습 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的例子

캔버스 게임 개발 학습 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)!


관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿