动机
最近重温黑客帝国,发现这个数字雨特效很炫酷,之前也看到网络上有相关类似的代码,我先自己思考了一种实现方式,最后参考网上给出的一种思路,最后写成了一个vue插件放在npm上,下面先上特效gif
是不是和电影里的很像,不过还是有点差距
自己的实现方法(失败)
对于这种较为复杂的动画特效,canvas是首选,当然css肯定也可以做,不过肯定超级复杂,代码量巨大。首先我第一眼看到这个特效,思路是这样的:
(1) 一般canvas用于绘制静态的图像,由于本例是动画效果,肯定得调用setTimeout或者setInterval或者raf,这里采用raf,不断绘制图像达到动态的效果,而且应当采用raf,它优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销
(2) 绘制一个黑色的背景
(3) 由于数字雨看上去是独立的一条条的向下运动的数字字母序列,因此我需要新建一个DigitRain类,里面设置了该数字雨的各种属性,来控制该条数字雨的运动特性,代码见下面
//数字雨类(参数是配置对象)
function DigitRain(configObj){
//数字雨的位置(x轴)
this.digitRainXPos = configObj.digitRainXPos,
//数字雨的位置(y轴)
this.digitRainYPos = configObj.digitRainYPos,
//数字雨的下落速度
this.rainVelocity = configObj.rainVelocity,
//数字雨的颜色
this.rainColor = configObj.rainColor,
//数字雨的拖尾长度
this.rainTailLength = configObj.rainTailLength,
//数字雨的文本内容
this.rainText = configObj.rainText,
...
}
复制代码
(4)然后写一个draw方法来控制其运动,最终在canvas里面调用fillText来画出文字
最终我写了一会发现困难太多,特别是文字拖尾效果的处理很麻烦,而且达不到效果,于是便作罢
换一种思路
参考了网上的一种思路,这种思路可谓是化繁为简,而且很容易理解,不得不佩服
(1) 同样是采用raf实现动画效果,首先根据canvas宽度和字体大小计算出雨滴下落的列数(宽度/字体大小),采用一个rainDropArray
(长度是列数)记录下每个列的文字的y轴的位置,初始都为0,核心数据结构就是这个rainDropArray
(2) requestAnimationFrame
的参数函数里,用for循环遍历rainDropArray
,然后用fillText
向canvas画上文字,x轴位置就是数组的index*字体大小,y轴位置就是rainDropArray[i]
的值,而且每次fillText都用封装的random方法获取字符串的随机数字字母
(3)拖尾效果的处理:这里很巧妙,对于拖尾效果,只需要在requestAnimationFrame
的参数函数里fillRect(0,0,.canvas.width,canvas.height)
即可,而fillStyle
设置为rgba(0,0,0,alpha)
,这样每次画图时都会画这么一个黑色背景,从而覆盖了之前画的字母,让字母颜色变淡,达到拖尾效果,通过控制alpha的值的大小来控制拖尾的长短,注意画图时没有用clearReact清除上次所画的内容,每次都是叠加上次所画的效果
requestAnimationFrame
每次只画了红圈内的字母,也就是对应每列的字母,其余颜色变淡的字母都是
requestAnimationFrame
以前画出来的,只不过被新画的黑色背景遮住了从而变暗,这样就完美的实现了拖尾效果 (4)最开始时
rainDropArray
的每个值都是0,且所有列下落速度一样,因此动画刚开始是会是如下效果
整整齐齐的下落,因此需要在字母到达canvas底部时做处理,让其有先后顺序,代码如下,触底后给定一定概率让其的y轴位置重新置位0,从而达到该列循环下落的效果
if(textYPostion>this.canvasHeight){
if(Math.random()>0.9){
this.rainDropPositionArray[i]=0;
}
}
复制代码
vue插件封装后的代码
总体代码量不多,不到150行,template部分就一个canvas
复制代码
github地址点这里