HTML5边玩边学汽车人变形.docx_第1页
HTML5边玩边学汽车人变形.docx_第2页
HTML5边玩边学汽车人变形.docx_第3页
HTML5边玩边学汽车人变形.docx_第4页
HTML5边玩边学汽车人变形.docx_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

一、状态及其保存和恢复在这一节开始之前,我们要先理解一下什么是状态以及状态的保存和恢复。玩过 MFC 编程的人经常能碰到这样的代码:pOldPen=pDC-SelectObject(pNewPen)我们在选择一个新画笔对象的同时,总是要保存住旧画笔对象,为什么要这样做呢?因为新画笔对象只是临时用一下,等用完了,我们想恢复到原来的画笔配置时,如果旧的配置事先没有被保存,这些配置就丢失了,也就没办法恢复了。在 HTML5 绘图中,某一刻的状态就是当前这一刻上下文对象的一系列属性的配置值,只是,决定一个画笔状态的属性比较少,如颜色、粗细、线型之类的,而确定上下文状态的属性比较多,包括下面这些:1、当前上下文对象的移动、旋转、缩放配置。2、当前上下文对象的 strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 属性值。3、当前上下文对象的裁剪路径配置。上面这一系列配置决定了当前这一刻上下文对象的状态,其中移动、旋转、缩放、globalCompositeOperation(组合)、裁剪下面我们马上会讲到。二、状态的保存与恢复上面我们说某一刻的状态由那么多属性决定,我们要保存这一刻的状态就要把这些属性值一个一个都保存,恢复的时候在一个一个都设置回去,那也太麻烦了。确实是这样的,所以上下文对下提供了了两个简单的方法,对状态进行保存和恢复,他们是:save( ) 和 restore( ),save 和 restore 方法可以多次调用,每调用一次 save 方法,调用时的状态(即一系列属性值)就压入一个栈中。每调用一次 restore 方法,最后一次 save 的状态就被恢复,即出栈。想象一下弹匣,第一颗被发射出去的子弹,总是最后一个被压入弹匣的。三、变型1、移动:translate(dx,dy)这个方法看上去很简单,其实它包含了一定的数学含义,你可以认为是整个坐标系的原点发生了移动,新坐标系下任意一点(x,y)相当于原坐标系下的坐标为:x=x+dxy=y+dy如果我们调用 ctx.translate(5,8) 改变上下文对象的坐标系状态,然后在新状态下的点(3,2)绘图,相当于图像被绘制到了原状态下的点(8,10)处,即:x=5+3=8y=5+2=10也许你会问,为什么要那么麻烦,直接在(8,10)处绘制比行吗?比如把:ctx.translate(5,8)ctx.drawImage(img,3,2)改成:ctx.drawImage(img,8,10)这样不是更简单、更直接吗?我的理解是,移动更多的情况下是为其他图形变换服务的,恰当的改变坐标原点可以让图形学计算更好理解,并带来很大方便,下面我举个简单的例子,假如:有一条线段 ,是 x 轴正向上的一小段:y = 0 (1 = x = 3),如果以坐标原点为圆心,逆时针旋转90度,则线段与 y 轴正向重合,旋转后的线段为:x = 0 (1 = y = 3)。但是我们不可能每次旋转都以原点为圆心进行旋转,假如我们以线段的一个端点(1,0)为圆心进行旋转,我们怎么才能得到旋转后线段上每一点的坐标值呢?其实这个过程可以分为三步:第一步:移动原点坐标到(1,0),新的线段依然在 x 轴上,但是方程变为了:y = 0 (0 = x = 2)。第二步:以新坐标系的原点为圆心进行旋转,得到新坐标系下的线段 x = 0 (0 = y = 2)。第三步:将新坐标系的原点移动到新坐标系下(-1,0)处,即将原点恢复到原来的位置,此时的线段为:x =1 (0 = y = 2)。第三步所得到的线段就是最后需要绘制的线段。从这个例子我们可以看出来,即使在这么简单的情况下,如果不移动坐标原点来直接计算旋转后的图形,也是比较困难的。提示:当你移动坐标原点之前,千万别忘了保存状态,当然,绘制完毕后也别放了恢复状态。2、缩放 scale(sx, sy)这个同样很简单,sx, sy 是缩放比例因子,缩放后新坐标系下任意一点 (x,y) 相当于原坐标系下的坐标为:x = x * sxy = y * sy同样,改变坐标系统总是不要忘记保存和恢复状态。3、旋转 rotate(A)angle 是旋转角度,旋转后新坐标系下任意一点 (x,y) 相当于原坐标系下的坐标为:x = x cosA - y sinAy = x sinA + y cosA同样,改变坐标系统总是不要忘记保存和恢复状态。4、变形 transform(m11, m12, m21, m22, dx, dy)其实前面讲的移动、缩放、旋转都是变形的特例,transform 方法的六个参数组成了一个变形矩阵,如下:m11m21dxm12m22dy001调用 transform 方法就相当于用这些参数为上下文对象设置了新的变形矩阵,关于变形矩阵的具体内容可以参照图形学相关资料,下面给出几个简单特例:移动 translate(dx,dy):相当于 transform(1,0,0,1,dx,dy)。缩放 scale(sx,xy):相当于 transform(sx,0,0,sy,0,0)。旋转 rotate(A):相当于 transform(cosA,sinA,-sinA,cosA,0,0)。以 (dx,dy) 为基准点旋转角度 A:transform(cosA, sinA, -sinA, cosA, dx(1-cosA) + dysinA, dy(1-cosA) - dxsinA)。以 (dx,dy) 为基准点进行 (sx,sy)比例缩放:transform(sx, 0, 0, sy, dx(1-sx), dy(1-sy)。还有很多其他更复杂的变形,大家可以参考图形学相关资料。下面给出一个基准点变形的例子,鼠标在图像上的某点保持按下状态,图像就会以该点为基准点进行缩放或者旋转,松开按钮后图像复原。提示:即缩放又旋转这个例子,并没有使用变形矩阵,而是用了四步简单变形复合而成。效果请查看文章下面全部代码。四、组合所谓组合就是一个图形绘制在另一个图形之上,会出现什么效果。默认的情况下是上面的图形覆盖了下面的图形,称之为 source-over 。上下文对象总共十二中组合类型,属性 globalCompositeOperation 被用来设置组合类型,如下:globalCompositeOperation = typetype 是下面 12 种字符串值之一:注意:上面所有例子中,蓝色方块是先绘制的,即“已有的 canvas 内容”,红色圆形是后面绘制,即“新图形”。五、裁剪路径在第一篇文章中我们就介绍了上下文对象的两大类绘制方法,即绘制线条的 stroke 系列方法和填充区域的 fill 系列方法,其实,上下文对象还有一类绘制方法叫做裁剪 clip 。什么是裁剪呢?打个不恰当的比方吧,你用一块布把电视机屏幕遮住了,这时候电视机屏幕上的任何变化你都看不见了。但是,如果你布上裁剪出一块区域,那么至少这块区域里的屏幕变化你能看见。当然裁剪区域之外的屏幕也在不停地变化(即也在重新绘制),只是你看不见罢了。这就是所谓的裁剪,平常在处理图像时经常会遇到这种需求。那么什么又是裁剪路径呢?上面说要在布上裁剪出一块区域,这块区域是怎么来的呢?这块区域是在裁剪动作 clip 之前,由绘图路径设定的,他可以是方形、圆形、五星形和其他任何可以绘制的轮廓形状。所以,裁剪路径其实就是绘图路径,只不过这个路径不是拿来绘图的,而是设定显示区域和遮挡区域的一个分界线。如果你不明白什么是绘图路径,在前的文章HTML5边玩边学(2):基础绘图中有介绍。下面的例子用了两种方法进行裁剪。第一种方法显示一来回移动的圆形裁剪区域,大体流程如下:1、清空画布2、改变圆心位置3、在新的圆心位置处设置一个圆形的裁剪区域4、在画布上绘制美女图像由于我们不停地在新位置处设定裁剪区域,我们就能看见裁剪区域在移动,而裁剪区域之外的图像并没有显示出来。我们用 clip 方法设置裁剪区域,之后绘制的图形只能显示裁剪区域内的一部分,而裁剪区域之外总是显示画布的背景色。假如并不想完全遮挡裁剪区域之外的图像,比如我们想让裁剪区域之内的图像完全显示出来,但是裁剪区域之外的图像以半透明的方式显示出来,该怎么做呢?这就要用到我们上面说的的组合知识了。第二中方法显示半透明的遮挡,大体思路如下:1、清空画布。2、将画布的所有区域用一种半透明的颜色填充,这里我用的是灰色,透明度0.9。3、改变圆心位置。4、在新的圆心位置处以 XOR 方式绘制圆,这样和圆形重叠的部分将被擦除掉。这时候我们得到的图形效果是一个半透明的画布,上面有一块完全透明的圆形区域。5、 在第 4 步的基础上,以 destination-over 方式绘制美女图像,这时候美女图像将会出现在第 4 步图形效果的下方,想象一下,正好是我们想要的效果吧?!效果请见代码如下: 你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器移动在图像上按住鼠标并移动基准点缩放在图像某点处按住鼠标基准点旋转在图像某点处按住鼠标br /& gt;基准点缩放同时旋转在图像某点处按住鼠标 你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 /美女图的 Base64 编码 IMG_SRC=data:image/gif;base64,/9j /4QDfRXhpZgAASUkqAAgAAAAFABIBAwA.;/省略四十字节 /= /基准点变形类 /= function Transform() /获取画布对象 this.ctx = document.getElementById(canvas1).getContext(2d); /创建图像对象 this.img=new Image(); /指定图像源 this.img.src=IMG_SRC; erval = null; /鼠标按钮状态 this.pressed=false; this.init(); /初始化图形 Ttotype.init=function() /鼠标按钮状态 this.pressed=false; /停止计时器 if(erval) clearInterval(erval); /变化值 this.delta = 0.06; /清空 this.ctx.clearRect(0,0,250,300); /重绘 this.paint(); /绘制图像 Ttotype.paint=function() var that=this; var img=this.img if(plete) that.ctx.drawImage(img,0,0); else var interval = setInterval(function() if(plete) that.ctx.drawImage(img,0,0); clearInterval(interval); ,300); /鼠标按钮按下后,开始变形 Ttotype.transform = function() /获取基准点 this.dx=event.offsetX; this.dy=event.offsetY; /获取基准点 this.startx=event.offsetX; this.starty=event.offsetY; /初始缩放比例 this.sc=1; /初旋转角度 this.angle=0; var that=this; if(document.getElementById(r1).checked) /鼠标按钮状态 this.pressed=true; else if(document.getElementById(r2).checked) erval = setInterval(function()that.scale(),50); else if(document.getElementById(r3).checked) erval = setInterval(function()that.rotate(),50); else erval = setInterval(function()that.scaleAndRotate(),50); /移动 Ttotype.translate = function() this.ddx=event.offsetX-this.startx; this.ddy=event.offsetY-this.starty; if(this.pressed) /清空 this.ctx.clearRect(0,0,250,300); /保存状态 this.ctx.save(); /平移 this.ctx.translate(this.ddx,this.ddy); /重绘 this.paint(); /绘制基准点 this.ctx.fillStyle=red; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); /恢复状态 this.ctx.restore(); /缩放变形 Ttotype.scale = function() /清空 this.ctx.clearRect(0,0,250,300); /改变缩放比例 this.sc=this.sc - this.delta; if(this.sc2) this.delta = -this.delta; /保存状态 this.ctx.save(); /以 (dx,dy) 为基准点进行 (sx,sy)比例缩放:transform(sx, 0, 0, sy, dx(1- sx), dy(1-sy) this.ctx.transform(this.sc, 0, 0, this.sc, this.dx*(1-this.sc), this.dy*(1-this.sc) /用新的变形矩阵重绘 this.paint(); /绘制基准点 this.ctx.fillStyle=red; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); /恢复状态 this.ctx.restore(); /旋转变形 Ttotype.rotate = function() /清空 this.ctx.clearRect(0,0,250,300); /改变缩放比例 var PI = Math.PI; this.angle=this.angle + PI/60; /保存状态 this.ctx.save(); /以 (dx,dy) 为基准点旋转角度 A:transform(cosA, sinA, -sinA, cosA, dx(1- cosA) + dysinA, dy(1-cosA) - dxsinA) this.ctx.transform(Math.cos(this.angle), Math.sin(this.angle), -Math.sin(this.angle), Math.cos(this.angle), this.dx*(1-Math.cos(this.angle) + this.dy*Math.sin(this.angle), this.dy*(1-Math.cos(this.angle) - this.dx*Math.sin(this.angle) /用新的变形矩阵重绘 this.paint(); /绘制基准点 this.ctx.fillStyle=red; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); /恢复状态 this.ctx.restore(); /即缩放又旋转变形,没有使用变形矩阵 Ttotype.scaleAndRotate = function() /清空 this.ctx.clearRect(0,0,250,300); /改变缩放比例 this.sc=this.sc - this.delta; if(this.sc2) this.delta = -this.delta; var PI = Math.PI; this.angle=this.angle + PI/60; /保存状态 this.ctx.save(); /先移动原点到基点 this.ctx.translate(this.dx,this.dy); this.ctx.scale(this.sc,this.sc); this.ctx.rotate(this.angle); this.ctx.translate(-this.dx,-this.dy); /用新的变形矩阵重绘 this.paint(); /绘制基准点 this.ctx.fillStyle=red; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); /恢复状态 this.ctx.restore(); var trans = new Transform(); /= function Clip() var canvas = document.getElementById(canvas3); this.ctx = canvas.getContext(2d); this.img=new Image(); this.img.src=IMG_SRC; /移动方向 this.delta=3,3; /起始点 this.pos_x = 225; this.pos_y = 120; /半径 this.radius = 40; /画布的长和宽 this.w = parseInt(canvas.getAttribute(width); this.h = parseInt(canvas.getAttribute(height); Ctotype.draw1=function() /碰撞检测 if (this.pos_x this.w - this.radius) this.delta0 = -(Math.random() % 4 + 5); if (this.pos_y this.h - this.radius) this.delta1 = -(Math.random() % 4 + 5); this.pos_x += this.delta0; this.pos_y += this.delta1; this.ctx.clearRect(0, 0, this.w, this.h); /保存状态 this.ctx.save() /移动变形 this.ctx.translate(this.pos_x,this.pos_y); /设置裁剪区域 this.ctx.beginPath(); this.ctx.arc(0 ,0,this.radius,0,Math.PI*2,true); this.ctx.clip(); / 将图片画到画布上 this.ctx.drawImage(this.img, -this.pos_x, -this.pos_y,this.w, this.h); /恢复状态 this.ctx.restore(); Ctotype.draw2=function() /碰撞检测 if (this.pos_x this.w - this.radius) this.delta0 = -(Math.random() % 4 + 5); if (this.pos_y this.h - this.radius) this.delta1 = -(Math.random() % 4 + 5); this.pos_x += this.delta0; this.pos_y += this.delta1; this.ctx.clearRect(0, 0, this.w, this.h); /绘制灰色的半透明蒙版 this.ctx.fillStyle=rgba(125,125,125,0.9) this.ctx.fillRect(0, 0, this.w, this.h); /保存状态 this.ctx.save() /移动坐标 this.ctx.translate(this.pos_x,this.pos_y); /裁剪透明的圆形区域 this.ctx.globalCompositeOperation = xor; this.ctx.fillStyle=white this.ctx.beginPath(); this.ctx.arc(0 ,0,this.radius,0,Math.PI*2,true); this.ctx.fill(); / 将图片画到蒙版的下面,即只露出透明区域 this.ctx.globalCompositeOperation = destination-over; this.ctx.drawImage(this.img, -this.pos_x, -this.pos_y

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论