《HTML5 Canvas开发详解》——2.7 简单画布变换

本节书摘来自异步社区《HTML5 Canvas开发详解》一书中的第2章,第2.7节,作者: 【美】Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.7 简单画布变换

画布变换是指用数学方法调整所绘形状的物理属性。缩放和旋转是常用的两个形状变换,本节将专门讨论。

所有变换都依赖于后台数学矩阵运算。幸运的是,读者只需使用变换功能而不必去理解这些运算。接下来,本书将讨论如何通过调整Canvas属性来应用旋转和缩放变换。

2.7.1 旋转和平移变换
首先指定画布上的对象面向左侧时处于角度为0的旋转状态(如果对象有“面”,这很重要,如果对象没有“面”,也可将其作为参照)。例如画一个方框(四边等长),它并没有一个与其他边相比而言初始就朝向左侧的“面”。现在画出来作为参考。

//绘制一个红色正方形
context.fillStyle = "red";
context.fillRect(100,100,50,50);

现在,如果将画布旋转45°,那么需要进行以下几步操作。首先,将Canvas变换设置为“identity”(或“reset”)矩阵。

context.setTransform(1,0,0,1,0,0);

由于Canvas使用弧度,而不是角度,在设定变换时应将45°角转成弧度。

var angleInRadians = 45 * Math.PI / 180;
context.rotate(angleInRadians);

第1课:变换在调用setTransform()或其他变换函数后应用到形状和路径上。
如果照抄这段代码运行,将发生很有趣的运行结果:屏幕上什么也没有!这是因为只有对画布应用setTransform()函数后才对形状起作用。示例中先绘制了一个正方形,然后设置变换属性。这将不会对这个使正方形发生改变(或者变换)。例2-7给出了能产生预想结果的正确代码顺序,结果如图2-12所示。

例2-7 简单旋转变换

function drawScreen(){

   //绘制一个红色正方形
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 45 * Math.PI / 180;
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(100,100,50,50);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换

这次得到了变换结果,但是估计和读者想要的结果有所不同。虽然红色的方框旋转了,但是好像画布也跟着一起旋转了。但实际上画布并没有旋转,context.rotate()函数调用后只有部分被绘制出来。那么,为什么这个正方形旋转到了屏幕外?因为旋转原点设在了“nontranslated”(0,0)点,所以导致了正方形从整个画布的左上角旋转。

例2-8提供了一个略微不同的场景:先画一个黑色方块,然后设置旋转变换,最后再画这个红色方块。结果如图2-13所示。

例2-8 旋转以及Canvas状态

function drawScreen(){

   //绘制黑色正方形
   context.fillStyle = "black";
   context.fillRect(20,20,25,25);

   //绘制红色正方形
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 45 * Math.PI / 180;
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(100,100,50,50);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换https://yqfile.alicdn.com/1e6128d4e58b795aefe6912c28d8e748eb7cd780.png" >

这个小黑方块没受旋转的影响,因为只有调用context.rotate()函数之后绘制的形状才受到影响。

同样,红方块移到了左侧之外。重申一下,这是因为画布不知道旋转的原点在哪儿而发生的。如果没有平移到实际的原点,Canvas会认为就在点(0,0),结果导致context.rotate()函数围绕点(0,0)旋转。这将会使读者领会到下面的内容。

第2课:只有将原点平移至形状中心,对象才会围着自己转。
将例2-8加以改变,使红色正方形能在旋转45°的同时保持位置不变。

首先,设置好控制红色正方形属性的fillRect()函数的变量。虽然不必这样做,但是这会使代码更易于阅读和调整。

var x = 100;
var y = 100;
var width = 50;
var height = 50;

接下来,使用context.translate()函数,把画布原点平移到红色正方形的中心点。这个函数可以将画布原点移到(x,y)处。这里将原点x坐标值设为红色正方形左上角的x值(100)加上其一半宽度。使用前面创建的变量即可控制这个红色正方形的属性,如下所示:

x+0.5*width

接下来,确定原点平移的y坐标值。这次使用左上角的y值和形状的高度值:

y+.05*height

translate()函数语句如下:

context.translate(x+.05*width, y+.05*height)

既然画布已经平移到了正确的原点,下面就可以进行旋转变换了,代码不变。

context.rotate(angleInRadians);

最后,绘制出形状。由于画布原点已经移动到将要绘制形状的位置,那么不能简单重复使用例2-8中同样的数值。现在要把(125,125)作为一切绘制操作的原点。125是从正方形左上角x值(100)加上一半宽度(25)而得来。y值同上。调用translate()方法实现这一目的。

绘制对象需要从正确的左上角坐标值(x,y)开始,进而从原点x值减去宽的一半,从y值减去高的一半。

context.fillRect(-0.5*width,-0.5*height, width, height);

为什么要这样?如图2-14所示。

试想一下,从左上角开始绘制正方形,如果原点在(125,125),左上角实际上是(100,100),然而原点已经平移过了,也就是说(125,125)现在相当于(0,0)。如果在未平移的画布上画这个方块,则应从(-25,-25)点开始。

这样必须把绘制正方形当成从(0,0)开始,而不是从(125,125)开始。因此,当实际绘图的时候必须使用这样的坐标,如图2-15所示。


《HTML5 Canvas开发详解》——2.7 简单画布变换

小结:变换需要将原点平移到正方形的中心,以使其围绕自己旋转,绘图的时候需要使代码将(125,125)当作实际的(0,0)点。如果不平移原点,则也可以使用(125,125)作为正方形的中心(如图2-14所示)。例2-9说明了其如何运行,结果如图2-16所示。

例2-9 围绕中心点旋转

function drawScreen(){

   //绘制黑色正方形
   context.fillStyle = "black";
   context.fillRect(20,20 ,25,25);

   //绘制红色正方形
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 45 * Math.PI / 180;
   var x = 100;
   var y = 100;
   var width = 50;
   var height = 50;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换

再看一个旋转的例子。例2-10在例2-9的基础上增加了4个单独的40×40的正方形,每个稍稍进行旋转,结果如图2-17所示。

例2-10 旋转多个正方形

function drawScreen(){

   //绘制一个红色正方形
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 45 * Math.PI / 180;
   var x = 50;
   var y = 100;
   var width = 40;
   var height = 40;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 75 * Math.PI / 180;
   var x = 100;
   var y = 100;
   var width = 40;
   var height = 40;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);

   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 90 * Math.PI / 180;
   var x = 150;
   var y = 100;
   var width = 40;
   var height = 40;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 120 * Math.PI / 180;
   var x = 200;
   var y = 100;
   var width = 40;
   var height = 40;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
}


《HTML5 Canvas开发详解》——2.7 简单画布变换

下面讲解缩放变换。

2.7.2 缩放变换
context.scale()函数有两个参数:第一个是x轴的缩放属性,第二个是y轴的缩放属性。一个对象的正常缩放大小数值是1。因此,如果要将一个对象放大一倍,可以将两个参数都设为2。在drawScreen()中使用下面这段代码可以产生这个红色正方形,如图2-18所示:

context.setTransform(1,0,0,1,0,0);
context.scale(2,2);
context.fillStyle = "red";
context.fillRect(100,100 ,50,50);


《HTML5 Canvas开发详解》——2.7 简单画布变换https://yqfile.alicdn.com/4df44e05f59c614e1012919fe16020d2ec553145.png" >

如果测试这段代码,读者将会发现缩放的工作方式与旋转差不多。由于没有平移原点来对正方形进行缩放,而是仍用画布左上角作为画布原点,结果红色的正方形向右下方移动了。如果从正方形的中心缩放,这就需要在缩放之前将原点平移到正方形中心,然后再围绕这个中心点绘图(如例2-9所示)。参见例2-11,结果如图2-19所示。

例2-11 从中心点缩放

function drawScreen(){

   //绘制一个红色正方形
   context.setTransform(1,0,0,1,0,0);
   var x = 100;
   var y = 100;
   var width = 50;
   var height = 50;
   context.translate(x+.5*width, y+.5*height);
   context.scale(2,2);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换

2.7.3 缩放和旋转组合变换
如果对对象进行缩放和旋转操作,Canvas变换可以轻松地组合并生成想要的效果(如图2-20所示),例2-12显示了如何在前面的示例中使用scale(2,2)和rotate(angleInRadians)进行组合变换。

例2-12 缩放和旋转组合

function drawScreen(){
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 45 * Math.PI / 180;
   var x = 100;
   var y = 100;
   var width = 50;
   var height = 50;
   context.translate(x+.5*width, y+.5*height);
   context.scale(2,2);
   context.rotate(angleInRadians);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换

例2-13也组合了旋转和缩放,这个例子是对矩形进行操作,如图2-21所示。

例2-13 非正方形对象的缩放和旋转

function drawScreen(){

   //绘制一个红色矩形
   context.setTransform(1,0,0,1,0,0);
   var angleInRadians = 90 * Math.PI / 180;
   var x = 100;
   var y = 100;
   var width = 100;
   var height = 50;
   context.translate(x+.5*width, y+.5*height);
   context.rotate(angleInRadians);
   context.scale(2,2);
   context.fillStyle = "red";
   context.fillRect(-.5*width,-.5*height , width, height);
  }


《HTML5 Canvas开发详解》——2.7 简单画布变换

找到任何形状的中心

对矩形或其他形状进行旋转和缩放与对正方形非常类似,实际上只要在缩放、旋转或者组合缩放旋转前将原点平移到形状的中心,都可以得到想要的效果。记住,任何形状的中心点都是半宽的x值和半高的y值!这需要使用边界框理论找到中心点。

图2-22说明了这个理论,尽管不是简单形状也可以找到包含对象任一点的边界框。图2-22接近正方形,但是同样符合矩形的边界框理论。


《HTML5 Canvas开发详解》——2.7 简单画布变换https://yqfile.alicdn.com/9cb2715a82873b4a97abf229a1f9119648489881.png" >
上一篇:Java面试之Jvm内存泄漏


下一篇:阿里云EDAS本地开发指南