前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTML5 canvas 粒子特效显示图像/文字

HTML5 canvas 粒子特效显示图像/文字

作者头像
用户5997198
发布2019-08-09 11:09:18
5.9K0
发布2019-08-09 11:09:18
举报
文章被收录于专栏:蚂蚁开源社区蚂蚁开源社区

本次实例将图片或文字分解成粒子。是先将图片或者文字画在canvas上,然后通过画布对象的getImageData获取到画布上的所有像素点,也就是imageData对象的data数组,存放着画布的所有像素的rgba值。

然后再遍历像素点,获取到当前像素点的rgba的a值也就是alpha透明度不为0,我直接舍弃了地透明度的,所以我写的判断是直接大于125就行了,255为不透明。

获取到粒子的位置后,就实例化出粒子对象,代码如下:

ctx.drawImage(img,this.imgx,this.imgy,img.width,img.height); var imgData=ctx.getImageData(this.imgx,this.imgy,img.width,img.height); for(var x=0;x<img.width;x+=particleSize_x){ for(var y=0;y<img.height;y+=particleSize_y){ var i=(y*imgData.width+x)*4; if(imgData.data[i+3]>=125){ var color="rgba("+imgData.data<i>+","+imgData.data[i+1]+","+imgData.data[i+2]+","+imgData.data[i+3]+")"; var x_random=x+Math.random()*20, vx=-Math.random()*200+400, y_random=img.height/2-Math.random()*40+20, vy; if(y_random<this.imgy+img.height/2){ vy=Math.random()*300; }else{ vy=-Math.random()*300; } particleArray.push(new Particle(x_random+this.imgx,y_random+this.imgy,x+this.imgx,y+this.imgy,vx,vy,color)); particleArray[particleArray.length-1].drawSelf(); } } }

将实例化的粒子对象扔进一个数组里保存起来。然后执行动画循环。

particleArray.sort(function(a,b){ return a.ex-b.ex; }); if(!this.isInit){ this.isInit=true; animate(function(tickTime){ if(animateArray.length<particleArray.length){ if(that.end>(particleArray.length-1)){ that.end=(particleArray.length-1) } animateArray=animateArray.concat(particleArray.slice(that.start,that.end)) that.start+=that.ite; that.end+=that.ite; } animateArray.forEach(function(i){ this.update(tickTime); }) }) }

animate方法的回调即为每次画布逐帧循环时调用的方法,其中animateArray就是真正用于放置于循环舞台的粒子对象,也就是上面demo中看到的从左到右一个一个粒子出现的效果,其实就是从particleArray中取粒子对象,在每一帧中扔几十个进animateArray中,所以就有了粒子一个一个出来的效果。

animate方法代码如下:

var tickTime=16; function animate(tick){ if(typeof tick=="function"){ var tickTime=16; ctx.clearRect(0,0,canvas.width,canvas.height); tick(tickTime); RAF(function(){ animate(tick) }) } }

这个代码就比较简单了,设置每一帧之间的时间差,我一般是设成16毫秒,这个就自己看哈,给tick方法传参循环。

在逐帧循环回调中,触发每个粒子对象的update,其中粒子的运动函数,绘画函数全部会由update函数触发。

下面这个是粒子对象的封装,其中x,y为粒子的位置,ex,ey为粒子的目标位置,vx,vy为粒子的速度,color为粒子的颜色,particleSize为粒子的大小,stop是粒子是否静止,maxCheckTimes和checkLength和checkTimes是检测粒子是否静止的属性,因为粒子在运动的时候,位置是无时无刻都在变化,所以是没有绝对静止的,所以需要手动检测是否约等于静止,然后再给予粒子静止状态,当粒子与目标位置的距离小于checkLength,并且在连续10帧的检测都粒子与距离目标都是小于checkLength,则说明粒子约等于静止了,将粒子的stop属性置为true,再接下来的动画逐帧循环中,对于stop为true的粒子则不进行运动计算:

function Particle(x,y,ex,ey,vx,vy,color){ this.x=x; this.y=y; this.ex=ex; this.ey=ey; this.vx=vx; this.vy=vy; this.a=1500; this.color=color; this.width=particleSize_x; this.height=particleSize_y; this.stop=false;this.maxCheckTimes=10; this.checkLength=5; this.checkTimes=0; } var oldColor=""; Particle.prototype={ constructor:Particle, drawSelf:function(){ if(oldColor!=this.color){ ctx.fillStyle=this.color; oldColor=this.color } ctx.fillRect(this.x-this.width/2,this.y-this.height/2,this.width,this.height); }, update:function(tickTime){ if(this.stop){ this.x=this.ex; this.y=this.ey; }else{ tickTime=tickTime/1000; var cx=this.ex-this.x; var cy=this.ey-this.y; var angle=Math.atan(cy/cx); var ax=Math.abs(this.a*Math.cos(angle)); ax=this.x>this.ex?-ax:ax var ay=Math.abs(this.a*Math.sin(angle)); ay=this.y>this.ey?-ay:ay; this.vx+=ax*tickTime; this.vy+=ay*tickTime; this.vx=~~this.vx*0.95; this.vy=~~this.vy*0.95; this.x+=this.vx*tickTime; this.y+=this.vy*tickTime; if(Math.abs(this.x-this.ex)<=this.checkLength&&Math.abs(this.y-this.ey)<=this.checkLength){ this.checkTimes++; if(this.checkTimes>=this.maxCheckTimes){ this.stop=true; } }else{ this.checkTimes=0 } } this.drawSelf(); this._checkMouse(); }, _checkMouse:function(){ if(!mouseX){ if(this.recordX){ this.stop=false; this.checkTimes=0; this.a=1500; this.ex=this.recordX; this.ey=this.recordY; this.recordX=null; this.recordY=null; } return; } var distance=Math.sqrt(Math.pow(mouseX-this.x,2)+Math.pow(mouseY-this.y,2)); var angle=Math.atan((mouseY-this.y)/(mouseX-this.x)); if(distance<mouseRadius){ this.stop=false; this.checkTimes=0; if(!this.recordX){ this.recordX=this.ex; this.recordY=this.ey; } this.a=2000; var xc=Math.abs((mouseRadius-distance)*Math.cos(angle)); var yc=Math.abs((mouseRadius-distance)*Math.sin(angle)); xc=mouseX>this.x?-xc:xc; yc=mouseY>this.y?-yc:yc; this.ex=this.x+xc; this.ey=this.y+yc; }else{ if(this.recordX){ this.stop=false; this.checkTimes=0; this.a=1500; this.ex=this.recordX; this.ey=this.recordY; this.recordX=null; this.recordY=null; } } } };

粒子的方法中,drawself为粒子的绘制自身的方法,画布的绘制对象的方法的调用次数越少,对整个动画的性能提升越大。因此,把粒子画成正方形,因为画正方形只需调用一个fillRect方法,而如果画圆形则需要先调用beginPath开始路径的绘制,再调用arc绘制路径,最后再通过fill填充颜色。性能方面肯定是画正方形性能更好,于是直接用fillRect。而也对粒子的color进行缓存,如果连续绘制的多个粒子颜色相同,就不用重复调用fillStyle方法更新画笔颜色。

然后是update方法,这个方法是粒子运动的核心,但是原理很简单,就是一些简单的运动学知识,获取到粒子与目标点夹角的角度,通过角度将粒子的加速度分解为水平和垂直加速度,再计算出粒子在新的一帧的水平速度和垂直速度,然后再通过新的速度计算出粒子新的位置,最后再绘制出来。update方法底部的if else则是判断粒子是否静止的代码。

粒子的最后一个方法,checkmouse其实就是检测鼠标位置,如果粒子跟鼠标的距离小于15,则将粒子的目标位置置于与鼠标距离为15的地方,为了保证鼠标移开后粒子还能回到原来的地方,所以用了个recordX和recordY来记录粒子初始的位置,当鼠标离开粒子时,重置粒子的目标位置。从而让粒子回到原来的位置。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 蚂蚁大喇叭 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档