Do what you do

canvas元素简易教程(11)

居然有好几个月没有更新博客了。。。我堕落了。。。

大家好,通过上一次的学习我们简单了解了非常重要的几个特性:坐标原点位移与状态保存。这两个特性对于canvas的使用有着非常多的帮助,在以后的学习中我们要渐渐接触更多与之有关的使用。同样的,在以后的使用中大家也会反复的使用这两个功能。所以希望大家可以多多练习,以熟练掌握它们。

今天我们来学习新的方法,首先是rotate方法,这个方法的作用是以中心为原点旋转canvas。在上次的学习中我们曾经对translate的使用进行了分析,曾经提起过以中心为原点的旋转问题,下面我们就准备来学习这个方法了。

rotate(angle)

这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。

旋转的中心点始终是canvas的原点,如果要改变它,我们需要用到translate方法。

似乎很简单对吧,其实就是很简单,不用想的太过于复杂了。我们来看一个例子吧:

function draw() {

  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.translate(75,75);

 

  for (var i=1;i<6;i++){ // Loop through rings (from inside to out)

    ctx.save();

    ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';

 

    for (var j=0;j<i*6;j++){ // draw individual 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();

  }

}

简单的说,内层循环中使用了rotate方法,以坐标中心为原点进行小圆点的绘制,外层循环中规定了离原点的绘制距离、每层的绘制个数以及颜色。也就是说,这里我们又用到了两层循环。第一层循环决定环的数量,第二层循环决定每环有多少个点。每环开始之前,都保存一下canvas的状态,这样恢复起来方便。每次画圆点,都以一定夹角来旋转canvas,而这个夹角则是由环上的圆点数目的决定的。最里层的环有6个圆点,这样,每次旋转的夹角就是360/6 = 60度。往外每一环的圆点数目是里面一环的2倍,那么每次旋转的夹角随之减半。相信这段简单的代码是难不倒大家的~

好了,我们继续下个知识点的学习,下一步我们将学习缩放。什么叫缩放不用我再讲一遍了吧,我们直接开始吧。

首先我还是来定义一下缩放的概念吧。所谓缩放。就是用来增减图形在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倍。

这个很简单吧,其实就是放大缩小嘛。我们借着一段代码来看一下吧:

function draw() {

  var ctx = document.getElementById('canvas').getContext('2d');

  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);  // no scaling

 

  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();

 

}

还记得以前画了好多螺旋曲线的那个方法吗?我们又要用它啦~

这最后的例子里,我再次启用前面曾经用过的spirograph方法,来画9个图形,分别赋予不同的缩放因子。左上角的图形是未经缩放的。黄色图案从左到右应用了统一的缩放因子(x和y参数值是一致的)。看下面的代码,你可以发现,我在画第二第三个图案时scale了两次,中间没有restore canvas的状态,因此第三个图案的缩放因子其实是0.75 × 0.75 = 0.5625。

第二行蓝色图案堆垂直方向应用了不统一的缩放因子,每个图形x方向上的缩放因子都是1.0,意味着不缩放,而y方向缩放因子是0.75,得出来的结果是,图案被依次压扁了。原来的圆形图案变成了椭圆,如果细心观察,还可以发现在垂直方向上的线宽也减少了。

第三行的绿色图案与第二行类似,只是缩放限定在横轴方向上了。

是不是很简单呢?只要抓住诀窍就能很容易拿下这个方法了。在这个问题上我们不做过多的讲解了,让我们来到今天的高潮—变形。。。

这是最恶心也是最难的一点了,因为,你需要会图形学的基础知识。。。

你不会啊,没事,我也不会,咱一起来学~

今天所学习的最后一个方法是允许直接对变形矩阵作修改:

transform(m11, m12, m21, m22, dx, dy)

这个方法必须将当前的变形矩阵乘上下面的矩阵:

m11         m21         dx

m12         m22         dy

0               0               1

如果任意一个参数是无限大,变形矩阵也必须被标记为无限大,否则会抛出异常。

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

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

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

理解了么?不理解?好吧,让我们换个思路来看这个东西,我会尽量给大家解释清楚的。

首先是当前的变形矩阵,当前的变形矩阵是什么啊,其实很简单:

 

x

y

1

这个就是当前的变形矩阵了。。。是不是后悔没有好好学习线代呢?

变形矩阵也就是横纵左边以及缩放倍数组成的一个矩阵,横纵坐标就是x与y,缩放倍数就是那个1。

首先让我们看一下平移:

当有:

x’=x+dx

y’=y+dy时:

x’      1       0       dx              x

y’=    0       1       dy     X     y

1       0       0       1                1

也就是说,可以使用context.transform (1,0,0,1,dx,dy)代替context.translate(dx,dy)。

也可以使用context.transform(0,1,1,0.dx,dy)代替。

然后是缩放:

当有:

x’=sx*x

y’=sy*y时:

x’      sx     0       dx              x

y’=    0       sy     dy     X     y

1       0       0       1                1

也就是说可以使用context.transform(sx,0,0,sy,0,0)代替context.scale(sx, sy),也可以使用context.transform (0,sy,sx,0, 0,0)代替。

最后是旋转:

B点通过A点逆时针旋转θ得到

x=r*cosa

y=r*sina

x’=r*cos(a+θ)=x*cosθ-y*sinθ

y’=r*sin(a+θ)=x*sinθ+y*cosθ

那么这个矩阵就是如下所示:

x’      cosθ          - sinθ         0                x

y’=    sinθ           cosθ          0       X     y

1       0                0                1                1

也即是说可以用context.transform(Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180),-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),0,0)可以替代context.rotate(θ)。

也可以使用context.transform(-Math.sin(θ*Math.PI/180),Math.cos(θ*Math.PI/180),

Math.cos(θ*Math.PI/180),Math.sin(θ*Math.PI/180), 0,0)替代。

好了,讲到这里我们是不是拿个例子来看一下呢?我想一个好的例子会帮助大家更好的理解它的:

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);

alert

    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);

}

这个例子首先用translate方法将相对中心移动到200,200,然后在循环中调用transform,以cos, sin, -sin, cos, 0, 0为参数进行刚才的旋转转化,就有了颜色逐渐变淡的以原点为圆心的一组矩形。最后调用了setTransform方法,进行本身的重置为单位矩阵,然后进行transform方法调用。transform的调用我相信大家已经清楚了,但大家或许会对重置为单位矩阵有所疑问。我的理解就是相对于原点先重置一个1*1的矩阵,那么你上次最后的那个图形不管在哪里,现在的相对目标都是以原点为起始点的一个单位矩阵。现在这么说,大家明白了吧~

好了,今天的东西就讲到这里,我们下次将会细致的讲解组合的属性,大家下次见~~~

posted @ 2013-03-18 12:22  key yao  阅读(2542)  评论(0编辑  收藏  举报
学会做事 学会做人