首先来看下我们准备要做的粒子动画效果是怎么样的~
是这样:
或者是这样:
甚至是这样:
很酷炫!
那如何去实现类似上面的粒子动画甚至根据自己的喜好去做更多其他轨迹的动画呢~请看下面详细的讲解。
因为粒子数量很多,而且涉及到图像像素处理,所以这里使用Canvas是不二选择。
注意,以下演示的代码只是关键代码,重点在于解决思路。
首先要在canvas画布上绘制一个由粒子组成的轮廓图,记录下每一个粒子的坐标,这样才能有后续的动画。
< canvas>是一个双标签元素,通过width和height的值来设置画布的大小。至于ctx(画布渲染上下文),可以理解为画布上的画笔,我们可以通过画笔在画布上随心所欲的绘制图案。如果浏览器不支持canvas会直接显示<canvas>标签中间自己设定的文字。当然<canvas>标签中间也可以是一张当不支持canvas时需要替换显示的图片。
2. 使用canvas的图像操作API绘制图像
绘制图像的关键API及参数说明:
引用MDN上的一张图会比较清晰的看出每个参数的作用:
drawImage就是把一个image对象或者canvas上(甚至是video对象上的的每一帧)指定位置和尺寸的图像绘制到当前的画布上。而在我们的需求中,是要把整个图像绘制到画布中。
对应浏览器看到的效果:
canvas有一个叫getImageData的接口,通过该接口可以获取到画布上指定位置的全部像素的数据:
把获取的imageData输出到控制台可以看到,imageData包含三个属性:
其中,width、height是读取图像像素信息完整区域的宽度和高度,data是一个Uint8ClampedArray类型的一维数组,包含了整个图片区域里每个像素点的RGBA的整型数据。这里必须要理解这个数组所保存像素信息的排序规则,请看下图描述的data数组:
每一个色值占据data数组索引的一个位置,一个像素有个4个值(R、G、B、A)占据数组的4个索引位置。根据数列规则可以知道,要获取第n个位置(n从1开始)的R、G、B像素信息就是:Rn = (n-1)*4 ,Gn = (n-1)*4+1 ,Bn = (n-1)*4+2 ,so easy~ 当然,实际上图像是一个包括image.height行,image.width列像素的矩形而不是单纯的一行到结束的,这个n值在矩形中要计算下:
由于一个像素是带有4个索引值(rgba)的,所以拿到图像中第i行第j列的R、G、B、A像素信息就是 Rij = [(j-1)*imageData.width + (i-1)]*4 ,Gij = [(j-1)*imageData.width + (i-1)]*4 + 1,Bij = [(j-1)*imageData.width + (i-1)]*4 + 2,Aij = [(j-1)*imageData.width + (i-1)]*4 + 3 。每个像素值都可以拿到了!
接下来就要把图像的粒子化轮廓图画出来了。那么,怎么做这个轮廓图呢,我们先读取每个像素的信息(用到上面的计算公式),如果这个像素的色值符合要求,就保存起来,用于绘制在画布上。另外,既然是做成粒子的效果,我们只需要把像素粒子保存一部分,展示在画布上。
具体做法是,设定每一行和每一列要显示的粒子数,分别是cols和rows,一个粒子代表一个单元格,那么每个单元格的的宽高就是imageWidth/cols和imageHeight/rows,然后循环的判断每个单元格的第一个像素是否满足像素值的条件,如果满足了,就把这个单元格的坐标保存到数组里,用作后续绘制图案用。
关键参考代码:
用完整代码做出的demo及效果:
至此,粒子轮廓图已经制作完成。
制作粒子动画分两种:
一种是粒子漂浮类,这种比较简单,只需要随机的改变每个粒子的位置值,然后一直执行setInterval或者requestAnimationFrame重绘画布即可,具体的效果因人喜好而去设定,就不具体讲解了,做了个简单的粒子漂浮的例子。
另一种是粒子的轨迹动画,这个相对复杂一些。这里要介绍的是每个粒子按照指定的轨迹在指定的时间内做位移,最终汇聚成指定图案的动画效果(也就是文章一开始的动效),要做成这类动画效果需要解决两个问题:一个是动画轨迹,另外一个是每个粒子执行动画的时机。
动画位移的轨迹,最常见的就是单位时间内改变固定的位移值,从而达到动画效果。但要做到炫酷的效果依赖这种单调固定的位移肯定是不行的。所以位移可以依赖缓动函数去做到单位时间内改变不一样的位移值,从而达到特别的效果。
一种是自己设定好控制点,然后通过贝塞尔曲线公式来计算每个单位时间的坐标值。
引用了wikipedia里面的图:
上面两个图都是在绘制一条特定曲线,可以看出二次曲线需要一个特定控制点P1,三次曲线需要两个特定控制点P1和P2来确定一条曲线,高阶曲线甚至需要更多的控制点来确定曲线轨迹。
求曲线的公式是根据德卡斯特里奥算法计算得来的,直接上公式。
二次曲线对应的公式:
三次曲线对应的公式:
从公式可以看出,只要确定控制点坐标、起始坐标和终点坐标后,就可以确定了一条曲线,然后就可以根据曲线公式求出每个时刻t对应的位置值B(t)。
当然使用这种方法需要自己去制定控制点坐标,计算也比较复杂,实现起来很繁琐。没事,我们还有别的办法确定曲线。
另外一种方法就是使用已有的缓动函数,不需要自己制定控制点,这里推荐出名的Tween算法的缓动函数,用其中一个缓动函数来介绍下参数值,其他缓动函数所传的参数值是一样的:
是不是觉得很熟悉?对没错,jquery用的动画扩展插件easing.js就是Tween算法的缓动函数。有了这现成的缓动函数,就可以制定粒子的起始点、终点(终点就是图案本身的坐标位置)以及动画执行持续时间来做我们要的效果。
关键参考代码:
根据参考代码做出一个效果:
嗯,动画效果是有了,但总感觉不太对劲。。。唔,仔细观察一下,是图案动画执行太过整体了,没有明显的颗粒动画效果,这就引出粒子动画的另一个关键点,粒子执行动画的时机。
要让粒子效果比较明显,那就不能让动画效果执行太过整体了,需要让图案上每个粒子有不同的时间间隔启动,根据一定的规律交错的执行动画。这里的粒子启动间隔有两种,一种是每一行粒子执行时间间隔,要让每一行的粒子启动时间有规律错开;另外一种是每一行粒子之间启动时间随机的错开,这样执行的粒子动画才会有一种层次感和每个粒子有独立动画的颗粒感。看下加了粒子启动时间间隔之后的效果对比:
比上面不加粒子启动时间间隔的效果好多了。
嗯,介绍差不多就是这样了,如果上面介绍的方法还是解决不了问题的话,还有办法。。。我把粒子动画效果和Tween的缓动函数一起封装了一下。直接配置一下就可以用了。 用法就是创建一个带有id的canvas,设定好宽度和高度,引入particle.min.js,然后配置一下参数即可, demo:
只有canvasId、imgUrl、cols、rows是必填的,其他参数都是根据需要自己选填。 ( ͡° ͜ʖ ͡°)✧