1

之前有调研过动画实现的方式,以及动画的种类(传送门:前端动画调研)。最近好好研究了一下svg的使用,在研究svg的同时又萌生了制作svg动画的想法,于是今天上场的主角就是使用JS驱动的动画库anime.js。文章前部分主要介绍使用anime.js完成对DOM元素的动画制作,后半部分会简单介绍一下anime.js与svg如何搭配使用。

1.Anime.js

Anime.js是简单,强大的轻量级JavaScript动画库,它可作用于css 属性,SVG,DOM 属性 以及JavaScript对象。根据官方的这段介绍,可以看出其对Canvas绘制是并不支持的,如果你需要对Canvas进行深度操作,推荐查看PixiJSCreateJS。通过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延时。

image.png

3.Timeline 时间线

Animation本身也自带timeline时间线的概念,但是很遗憾,支持度非常的差,就连chrome支持度也不是很好
image.png。那么什么是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内置多种对于动画监听的事件方法,


Parkeeers
780 声望32 粉丝

求知若渴,保持谦逊


引用和评论

0 条评论