之前有调研过动画实现的方式,以及动画的种类(传送门:前端动画调研)。最近好好研究了一下svg的使用,在研究svg的同时又萌生了制作svg动画的想法,于是今天上场的主角就是使用JS驱动的动画库anime.js。文章前部分主要介绍使用anime.js完成对DOM元素的动画制作,后半部分会简单介绍一下anime.js与svg如何搭配使用。
1.Anime.js
Anime.js是简单,强大的轻量级JavaScript动画库,它可作用于css 属性,SVG,DOM 属性 以及JavaScript对象。根据官方的这段介绍,可以看出其对Canvas绘制是并不支持的,如果你需要对Canvas进行深度操作,推荐查看PixiJS,CreateJS。通过anime.js我们可以使用js去编写原本需要在css中书写很长的keyframes,也可以通过使用timeline来同时处理多个元素的动画效果,这一切都不需要再编写css。看一段简单的代码:
// 引入animejs
import anime from 'animejs/lib/anime.es.js';
// 初始化anime方法,即可对指定元素激活动画效果
anime({
targets: 'div',
translateX: 250,
rotate: 360,
backgroundColor: '#FFF',
duration: 800
});
上述代码的意味着,当调用anine()方法时,便将所有的div标签立即执行向右移动250px,同时旋转360deg,并将颜色同时修改为白色,整个动画持续800ms。需要注意的是,targets默认使用document.querySelectorAll(),所以这里所有的div标签都会注入刚才定义的动
通过这段代码是不是觉得定义一个动画非常的简单,有种将css代码放入JS对象中的感觉。那么anime.js是如何实现关键帧keyframes,接着往下看。
2.keyframes关键帧
在css中,我们可以通过transition实现线性的动画,通过设置duration以及作用的元素,即可实现一段简单的如放大缩小动画。
https://jsfiddle.net/o02yda5j/
然而如果需要实现更加复杂的非线性多段动画,必然要使用animations,借助@keyframes也可以实现较为复杂的动画,这里有个简单栗子
https://jsfiddle.net/o02yda5j/
在这段代码中,我们将动画设置为4帧,translate分别在每一帧中定义一次,如果使用anime.js声明多帧将变得非常简单且清晰:
anime({
targets: 'div',
translateX: [
{ value: 200 },
{ value: 200 },
{ value: 0 }, // 第三帧
{ value: 0 },
],
translateY: [
{ value: 0 },
{ value: 100 },
{ value: 100 }, // 第三帧
{ value: 0 },
],
})
我们还可以在每一帧定义持续时间,延时,以及贝塞尔曲线。如果没有对每一帧定义持续时间duration,该帧将会与其他未声明duration的帧平分animation的duration。不知道你是否注意到,通过在每一帧设置不同的延时delay,配合不同的css属性,将会很轻松就实现非常复杂的动画。不过缺点也非常明显,就是需要计算每一帧的duration+delay对下一帧的影响,因为动画本身就是不同的元素在不同的节点发生的变形构建的。即便如此,在anime.js手动计算两者对下一帧的影响,依然要比@keyframes写法更为高效直观。如下代码可以实现一个小球以不同的贝塞尔曲线进行矩形运动。
anime({
targets: '.property-keyframes-demo .el', // 指定动画元素,省去了css多少的代码...
translateX: [ // 只参与两帧,需要注意,每个css属性不要求帧数都一样
{ value: 250, duration: 1000, delay: 500 },
{ value: 0, duration: 1000, delay: 500 }
],
translateY: [
{ value: -40, duration: 500 },
{ value: 40, duration: 500, delay: 1000 },
{ value: 0, duration: 500, delay: 1000 }
],
scaleX: [
{ value: 4, duration: 100, delay: 500, easing: 'easeOutExpo' }, // 第一帧发生在动画开始的500ms后,持续时间为100ms,贝塞尔曲线为easeOutExpo
{ value: 1, duration: 900 },
{ value: 4, duration: 100, delay: 500, easing: 'easeOutExpo' },
{ value: 1, duration: 900 }
],
scaleY: [
{ value: [1.75, 1], duration: 500 },
{ value: 2, duration: 50, delay: 1000, easing: 'easeOutExpo' },
{ value: 1, duration: 450 },
{ value: 1.75, duration: 50, delay: 1000, easing: 'easeOutExpo' },
{ value: 1, duration: 450 }
],
rotate: [
{ value: 1.2 },
],
width: [
{ value: 200 },
],
easing: 'easeOutElastic(1, .8)',
loop: true
});
但是anime的keyframes也不是万能的,如果你的动画写的足够复杂,那么势必帧数就要增大,再加上多个css属性在每一帧如果都有体现,如上图,计算同一属性的不同帧,以及不同属性的不同帧,将会异常困难。因为你总是无法准确统计每一帧duration+delay对其他帧的影响,加入你想修改某一帧的duration,你就需要修改整个动画的受其影响的每一帧。如何解决,我们引入timeline,将复杂动画的过程以时间线的形式进行切片,分段处理。换句话讲使用timeline,将动画由串型改为并行。下图表示不同属性不同帧的执行情况,水平黑线表示duration,其白色间隔为delay延时。
3.Timeline 时间线
Animation本身也自带timeline时间线的概念,但是很遗憾,支持度非常的差,就连chrome支持度也不是很好
。那么什么是timeline呢,学过视频剪辑的同学应该对这个很熟悉,简单讲,通过关键帧keyframes,一个完整的动画可以被分成若干个部分去执行,但是仅仅是一个元素的动画;timeline的基础单位是一个完整的动画,它是由一个一个动画组成的整体动画,允许你将多个动画进行组合,这是区别keyframes的根本。
详细的内容,官方文档以及实例解释的很清楚了,这里想说明几个使用中关键的地方。
1. timeline timeoffset
初始化一个时间线代码如下:
const timeline_1 = anime.timeline({
easing: 'easeOutExpo',
duration: 750
});
timeline_1
.add(paramters, timeoffset)
.add(paramters, timeoffset)
.add(paramters, timeoffset);
需要强调的是timeoffset参数,默认timeline上的每个动画是串型执行,即上个动画执行完毕,下个动画才能执行;但是好在add方法第二个参数允许声明各个timeline上的动画执行时间,这样就可以实现多个动画元素并行执行,对于构建复杂的动画体验极好。
2. 什么时候使用timeline
虽然使用一个timeline就可以实现复杂的动画,但是对于复杂的动画,使用一个timeline同样避免不了写一大堆动画元素内的keyfreams,所以对于复杂的动画,建议引入多个timeline,这样每个动画里各个帧的时间参照线就会随着新创建的timeline而清空,所以写keyframes更为友好。
4. 动画事件方法
对于复杂的动画,如果遇到需要监听动画各个状态怎么破,不要担心,anime.js内置多种对于动画监听的事件方法,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。