加载中…
个人资料
陈在真Sunny_chen
陈在真Sunny_chen
  • 博客等级:
  • 博客积分:0
  • 博客访问:207,097
  • 关注人气:183
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Canvas之动态波浪效果

(2012-04-26 09:04:51)
标签:

javascript

倒影

security_err

js1k

save

getimagedata

putimagedata

分类: Canvas
不知不觉离上一篇文章发布已经快三个礼拜,工作的时候时间过的很快尤其是有事做的时候...随着项目周期更迭,今天休渔...休渔真好...因为前阵子收藏着这个网站,现在就翻出来看:JS1K 里面真的很多东西看很多东西学...比如这棵圣诞树、比如这朵玫瑰花、比如这个秋天的场景 ...我这个动态波纹就是从这里领悟到的,或者直接说抄袭到,体会到东西就要分享出来,所以虽然代码很短...真的很短才20行不到,而核心代码就一句...实现的具体效果如下所示:

Canvas之动态波浪效果

你可以通过这个链接,下载我的源代码:《Canvas之动态波浪效果

为方便下载,提供本人网盘帐号密码,请不要弄乱里面的页面,以方便其它人下载,谢谢。

帐号:287019674@qq.com

密码:123456


(在这里说明一下,这里涉及到canvas的像素化操作,在FF或者chrome操作可能会出现一个SECURITY_ERR,这是因为Firefox与Chrome对本地文件JavaScript访问权限控制比较严格,你需要在服务器环境下运行代码,而safari是可以在普通环境下正常运行的。)

下面是对效果实现的分析(因为代码很短,所以我这里贴出来):

var canvas=document.getElementByIdx_x_x("canvas");
var ctx=canvas.getContext("2d");

var image=new Image();
image.src="head.jpg";
var t=0;
var height=120;
var timer=setInterval(function(){
    ctx.clearRect(0,0,600,500);
    ctx.drawImage(image,0,0);
    t++;
    for(var i=height;i>0;i--){
        ctx.putImageData(ctx.getImageData(0,180-i+Math.sin(t/5+i/10)*i/10,180,1),Math.sin(t/5+i/10)*i/10,180+i-1);
    }
   
},100);

通过ID获取到canvas对象,然后再获取到2d绘图上下文,新建一个图片对象,设置它的图片地址。
其中t是时间变量,height是倒影的高度(0-180)。

写一个setInterval跟t变量循环产生动态波纹,每次绘制的时候都先用clearRect清空画布,再重新画倒影实体跟倒影。

最初看到原作品的想法是通过画布变换rotate方法来旋转,效果如下:

Canvas之动态波浪效果

很明显,这样整出来的效果是“旋转”,而不是“倒影”,这个旋转效果的具体代码如下,你可以作为参考:

ctx.drawImage(image,0,0);
ctx.save();
ctx.translate(180,180);
ctx.rotate(Math.PI);
ctx.drawImage(image,0,-180);
ctx.restore();

里面的save是保存当前画布状态,restore是回复保存时画布的状态。也许你会问,那能保存什么状态呢?

canvas的save可以保存这些内容内容:transformation matrix、clipping region、globalAlpha、globalCompositeOperation、strokeStyle、fillStyle、 lineWidth、lineCap、lineJoin、miterLimit、shadowOffsetX、shadowOffsetY、 shadowBlur、shadowColor、font、textAlign、textBaseline……

这里只是做个引申,回归正题,怎么实现倒影的问题上。

这个问题的解决方案不是我想出来的,是《Autumn Evening》的作者Philip Buchanan想出来的,当然不知道他是不是在哪里抄的哈...在解释原理之前,先解释下getImageData、putImageData、ImageData、CanvasPixelArray:

context.getImageData(x, y, width, height)
用来获取画布的指定范围内的 ImageData 对象,并返回此对象

x - 需要获取的 ImageData 对象矩形框相对于画布的左上角的 x 坐标
y - 需要获取的 ImageData 对象矩形框相对于画布的左上角的 y 坐标
width - 需要获取的 ImageData 对象矩形框的宽
height - 需要获取的 ImageData 对象矩形框的

putImageData(imagedata, x, y, [dirtyX, dirtyY, dirtyWidth, dirtyHeight])
用来对画布上写入指定的数据
imagedata - 需要写入的 ImageData 对象
 x, y - 指定需要写入的 ImageData 对象矩形框相对于画布左上角的坐标
[dirtyX, dirtyY, dirtyWidth, dirtyHeight] - 显示 ImageData 对象上指定范围内,如果不填,则设置为0,0,imagedata.width,imagedata.height

ImageData - 像素对象
width - 像素对象的宽
height - 像素对象的高
data - 像素数据,CanvasPixelArray 类型的对

CanvasPixelArray
像素数据对象,可以数组方式进行操作,数组中每 4 个元素代表一个像素(即RGBA),每个元素为一个字节,4 个元素按照 rgba 的方式描述一个像素
length - 元素总数,即像素总数

倒影原理是这样的:从上往下获取投影区域所对应的实体区域,由下往上铺,看下图应该好理解点:

Canvas之动态波浪效果

先获取绿色那条线所在区域的的像素数据,然后放在下面的绿色线区域,接着获取蓝色线所在区域的的像素数据,然后放在下面的蓝色线区域,这样连续的获取再从下往上平铺,这样最后就会形成一个倒影的效果,最原始的静态倒影代码如下:

for(var i=height;i>0;i--){
        ctx.putImageData(ctx.getImageData(0,180-i,180,1),0,180+i-1);
}

而Math.sin是一个正弦函数,让倒影产生一个正弦偏移,Math.sin()*i/10是扩大振幅,让波动更明显,而使用"*i/10"而不用一个常数的目的是让波动由下至上波动振幅逐步减小,如果用一个常数的话,效果是这样的:

Canvas之动态波浪效果

在倒影与实体之间会产生偏移,接合的很恶心...而"t/5+i/10"里面的t作为一个时间变量,是用来控制正弦的变化,让波纹能动态的展现,这样基本的效果就出来了,还不错,但是看到秋天那里在getImageData里面还加了个Math.sin,有点奇怪,就跟着加加看,结果,很赞...如图:

Canvas之动态波浪效果

倒影出现了一个拉伸扭曲效果,效果更逼真,未加之前获取到的数据是‘一对一’,加了之后行的获取高度设置为

180-i+Math.sin(t/5+i/10)*i/10

这样随着正弦的计算,会在局部产生一个‘一对多’的现象,在实体的一行数据可能会对应倒影的多行数据,这样就给拉伸了,而在倒影的时候又同时有Math.sin的存在,两者同步,所以在拉伸的同时扭曲,效果很正...

ctx.putImageData(ctx.getImageData(0,180-i+Math.sin(t/5+i/10)*i/10,180,1),Math.sin(t/5+i/10)*i/10,180+i-1);

可以自己修改参数改变它的振幅,倒影高度,拉伸幅度的细节,从而实现新的效果。

一句代码就揉合了这么多的细节操作,实在太精华了,我继续分析,希望领悟到更多的精华分享出来...







0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有