前端动画选择

零、前言

动画的种类。一般遇到的动画主要分为两类:展示性动画,交互性动画。展示动画一般是页面局部的动画效果,例如动态logo展示,部分icon的效果展示。交互性动画例如红包雨。

前端实现动画往往要带来比较大的工作量,动画的堆叠,以及调试,使用合理的框架将会大大减少工作量。

主要介绍一下几种(star比较高的)框架

  • Animate.css
  • Lottie
  • Anime.js

一、动画的实现方式

  • CSS Animation

  • JS驱动的动画

  • canvas

  • SVG

CSS3动画,从直观上来讲,可以说是十分丝滑了,使用简单,深入研究请参考:https://www.w3cplus.com/animation/animation-performance.html 。

JS驱动的动画因为要定时更新DOM来实现动画效果,性能较差(requestAnimationFrame的性能表现要优于setXXX系列,深入研究请访问:https://jinlong.github.io/2013/06/24/better-performance-with-requestanimationframe/ 同时应尽量使用transform,开启硬件加速,避免引起页面重拍)。

SVG,本质是解析XML结构,通过数据修改dom后,浏览器会自动重绘(区别于canvas),性能优于JS驱动,对比canvas还是存在劣势。

canvas可以启用硬件加速,性能较优。与CSS3动画对比,chrome的canvas性能优于css (参考数据来源:https://segmentfault.com/a/1190000000438057)。

二、Animate.css

纯CSS3框架,https://github.com/daneden/animate.css。

提供了初始的css3类库,使用时直接加上类名即可。也可以在样式里层叠掉默认样式进行微调。

优点

  1. 使用简单粗暴
  2. 预设效果多
  3. 性能靠谱

缺点

  1. 灵活性较低
  2. 难以实现复杂动画序列

适用场景

  1. 在原有页面上进行动画增加、优化
  2. 不涉及复杂动画逻辑

三、Lottie

http://airbnb.io/lottie/
https://github.com/airbnb/lottie-web
https://github.com/chenqingspring/react-lottie

UI通过AE使用bodymovin导出json,前端直接调用,可以选择导出canvas、svg。兼容多端。react项目推荐使用react-lottie

event-node项目 PT-7306_20180926_animate 分支 /animation/index.htm

以React为例:

简单用法

const { json } = this.props.store.toJS();
const options = {
    renderer: 'svg',
    loop: false,
    autoplay: false,
    animationData: json.deom1
}
return (
    
{ this.singalLottie = ref; }} height={'100vw'} width={'100vw'}/>
)

但是不知道为什么pause是失效的= =之后研究下。
官方示例使用的是props里面的isStopped和isPaused,页面存在单一控制动画可以使用。
序列例子:

const { json } = this.props.store.toJS();
const options = {
    renderer: 'canvas',
    loop: false,
    autoplay: false,
    animationData: json.deom1
}
const optionFirst = {
    renderer: 'canvas',
    loop: false,
    autoplay: true,
    animationData: json.deom1
}
return (
    
{ this.moreLottie2.play() } } ]} /> { this.moreLottie2 = ref; }} style={{display: 'inline-block'}} height={'50vw'} width={'50vw'} eventListeners={[ { eventName: 'complete', callback: () => { this.moreLottie3.play() } } ]}/> { this.moreLottie3 = ref; }} style={{display: 'inline-block'}} height={'50vw'} width={'50vw'} eventListeners={[ { eventName: 'complete', callback: () => { this.moreLottie4.play() } } ]}/> { this.moreLottie4 = ref; }} style={{display: 'inline-block'}} height={'50vw'} width={'50vw'}/>
)

常用方法:
animation.play(); // 播放该动画,从目前停止的帧开始播放
animation.stop(); // 停止播放该动画,回到第0帧
animation.pause(); // 暂停该动画,在当前帧停止并保持
animation.goToAndStop(value, isFrame); // 跳到某个时刻/帧并停止。isFrame(默认false)指示value表示帧还是时间(毫秒)
animation.goToAndPlay(value, isFrame); // 跳到某个时刻/帧并进行播放
animation.goToAndStop(30, true); // 跳转到第30帧并停止
animation.goToAndPlay(300); // 跳转到第300毫秒并播放
animation.playSegments(arr, forceFlag); // arr可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段
animation.playSegments([10,20], false); // 播放完之前的片段,播放10-20帧
animation.playSegments([[0,5],[10,18]], true); // 直接播放0-5帧和10-18帧
animation.setSpeed(speed); // 设置播放速度,speed为1表示正常速度
animation.setDirection(direction); // 设置播放方向,1表示正向播放,-1表示反向播放
animation.destroy(); // 删除该动画,移除相应的元素标签等。在unmount的时候,需要调用该方法

事件:
data_ready: 加载完json动画
complete: 播放完成(循环播放下不会触发)
loopComplete: 当前循环下播放(循环播放/非循环播放)结束时触发
enterFrame: 每进入一帧就会触发,播放时每一帧都会触发一次,stop方法也会触发
segmentStart: 播放指定片段时触发,playSegments、resetSegments等方法刚开始播放指定片段时会发出,如果playSegments播放多个片段,多个片段最开始都会触发。
data_ready: 动画json文件加载完毕触发
DOMLoaded: 动画相关的dom已经被添加到html后触发
destroy: 将在动画删除时触发

优点:
1.可以实现较为复杂动画
2.可以导出canvas,性能高。也可以在部分小图标上导出svg,灵活性高。
3.方便控制启停,可以控制回调
4.大大解放前端生产力

缺点:
1.需要UI部门支持 (咨询了下 环境配置较为复杂,配好之后使用起来还是ok的)
2.部分效果导出无法实现

适用场景:
1.展示性动画,动画序列不长,可控
2.UI给力

四、Anime.js

http://animejs.com/documentation
https://github.com/juliangarnier/anime

demo:https://event.qunar.com/hrPropaganda/index.htm
Anime.js是一个JS驱动的动画框架,支持:

  • 任何包含数值的DOM属性都可以设置动画(包含input的value)
// 1~1000
anime({
  targets: '#domAttributes input',
  value: 1000,
  round: 1, 
  easing: 'easeInOutExpo'
});
  • kerframes,连接多个动画
anime({
  targets: '#keyframes .el',
  translateX: [
    { value: 250, duration: 1000, delay: 500, elasticity: 0 }, //第一步
    { value: 0, duration: 1000, delay: 500, elasticity: 0 } //第二步
  ]
});
  • Timeline
var basicTimeline = anime.timeline();

basicTimeline
  .add({
    targets: '#basicTimeline .square.el',
    translateX: 250,
    easing: 'easeOutExpo'
  })
  .add({
    targets: '#basicTimeline .circle.el',
    translateX: 250,
    easing: 'easeOutExpo'
  })
  .add({
    targets: '#basicTimeline .triangle.el',
    translateX: 250,
    easing: 'easeOutExpo'
  });
  • 动画状态控制,播放、暂停、重新开始等(http://animejs.com/documentation/#playPause)
var playPause = anime({
  targets: '#playPause .el',
  translateX: 250,
  delay: function(el, i, l) { return i * 100; },
  direction: 'alternate',
  loop: true,
  autoplay: false
});

document.querySelector('#playPause .play').onclick = playPause.play;
document.querySelector('#playPause .pause').onclick = playPause.pause;
  • 动画状态回调,开始,执行中,结束提供回调函数
var runLogEl = document.querySelector('#run .current-time-log');
var runProgressLogEl = document.querySelector('#run .progress-log');

var run = anime({
  targets: '#run .el',
  translateX: 250,
  delay: 1000,
  run: function(anim) {
    runLogEl.value = 'running';
    runProgressLogEl.value = 'progress : ' + Math.round(anim.progress) + '%';
  },
  complete: function(anim) {
    runLogEl.value = 'not running';
    runProgressLogEl.value = 'progress : 100%';
  }
});
  • 自定义贝塞尔曲线
  • 支持promise,动画结束后,调用anime.finshed会返回一个promise对象
var finishedLogEl = document.querySelector('#finishedPromise .finished-log');

var finishedPromise = anime({
  targets: '#promises .el',
  translateX: 250,
  delay: 1000
});

var promise = finishedPromise.finished.then(logFinished);

function logFinished() {
  finishedLogEl.value = 'Promise resolved';

  // Rebind the promise, since this demo can be looped.
  setTimeout(function() {
    promise = finishedPromise.finished.then(logFinished);
  });
}

finishedPromise.update = function(anim) {
  if (!anim.completed) {
    finishedLogEl.value = '';
  }
}
  • 支持svg

业务中常见的使用为时间序列,每一个add()执行完之后,才会执行下一个add的内容,如果期望多个动画同时执行,可以使用offset

// 下列三个同时执行
.add({
    targets: this.caihua1,
    duration: 1000,
    translateY: '7vh'
})
.add({
    targets: this.caihua2,
    duration: 1000,
    translateY: '-7vh',
    offset: '-=1000'
})
.add({
    targets: [this.tiao1, this.tiao2],
    duration: 1500,
    opacity: 1,
    offset: '-=1000'
})
Tables Examples Infos
+= '+=100' Starts 100ms after the previous animation ends
-= '-=100' Starts 100ms before the previous animation ends
*= '*=2' Starts at 2 times the previous animations duration

优点:
1.实现了CSS3动画的深度封装
2.通过js驱动来操作动画状态,实现了对于多个动画分支的管理,利于实现复杂动画

缺点:
难以处理交互性动画(本篇文章暂时不涉及)

适用场景:
复杂动画场景,需要定制各个细节,UI难以出AE导出的json的时候

你可能感兴趣的:(前端动画选择)