简介
动画这个概念非常宽泛,涉及各个领域,这里我们把范围缩小到前端网页应用层面上,不用讲游戏领域的Animate,一切从最简单的开始。
目前大部分网页应用都是基于框架开发的,比如Vue,React等,它们都是基于数据驱动视图的,那么让我们来对比一下,还没有这些框架的时候我们如何实现动画或者过渡效果,然后使用数据驱动又是如何实现的。
传统过渡动画
动画效果对体验有着非常重要的效果,但是对于很多开发者来讲,可能是个非常薄弱的环节。在css3出现之后,很多初学者最常用的动画过渡可能就是css3的能力了。
css过渡动画
css启动过渡动画非常简单,书写transition属性就可以了,下面写一个demo
.normal {
width: 100px;
height: 100px;
background-color: red;
transition: all 0.3s;
}
.normal:hover {
background-color: yellow;
width: 200px;
height: 200px;
}
效果还是很赞的,css3的transition基本满足了大部分动画需求,如果不满足还有真正的css3 animation。
大名鼎鼎的css动画库,谁用谁知道。
不管是css3 transition 还是 css3 animation,我们简单使用都是通过切换class类名,如果要做回调处理,浏览器也提供了 ontransitionend , onanimationend等动画帧事件,通过js接口进行监听即可。
var el = document.querySelector('#app')
el.addEventListener('transitionstart', () => {
console.log('transition start')
})
el.addEventListener('transitionend', () => {
console.log('transition end')
})
ok,这就是css动画的基础了,通过js封装也可以实现大部分的动画过渡需求,但是局限性在与只能控制css支持的属性动画,相对来说控制力还是稍微弱一点。
js动画
js毕竟是自定义编码程序,对于动画的控制力就很强大了,而且能实现各种css不支持的效果。 那么 js 实现动画的基础是什么?
简单来讲,所谓动画就是在 时间轴上不断更新某个元素的属性,然后交给浏览器重新绘制,在视觉上就成了动画。废话少说,还是先来个栗子:
// Tween仅仅是个缓动函数
var el = document.querySelector('#app')
var time = 0, begin = 0, change = 500, duration = 1000, fps = 1000 / 60;
function startSport() {
var val = Tween.Elastic.easeInOut(time, begin, change, duration);
el.style.transform = 'translateX(' + val + 'px)';
if (time <= duration) {
time += fps
} else {
console.log('动画结束重新开始')
time = 0;
}
setTimeout(() => {
startSport()
}, fps)
}
startSport()
在时间轴上不断更新属性,可以通过setTimeout或者requestAnimation来实现。至于Tween缓动函数,就是类似于插值的概念,给定一系列变量,然后在区间段上可以获取任意时刻的值,纯数学公式,几乎所有的动画框架都会使用,想了解的可以参考张鑫旭的Tween.js
OK,这个极简demo也是js实现动画的核心基础了,可以看到我们通过程序完美的控制了过渡值的生成过程,所有其他复杂的动画机制都是这个模式。
传统和Vue/React框架对比
通过前面的例子,无论是css过渡还是js过渡,我们都是直接获取到 dom元素的,然后对dom元素进行属性操作。
Vue/React都引入了虚拟dom的概念,数据驱动视图,我们尽量不去操作dom,只控制数据,那么我们如何在数据层面驱动动画呢?
Vue框架下的过渡动画
可以先看一遍文档
我们就不讲如何使用了,我们来分析一下Vue提供的transition组件是如何实现动画过渡支持的。
transition组件
先看transition组件代码,路径 “src/platforms/web/runtime/components/transition.js”
核心代码如下:
// 辅助函数,复制props的数据
export function extractTransitionData (comp: Component): Object {
const data = {}
const options: ComponentOptions = comp.$options
// props
for (const key in options.propsData) {
data[key] = comp[key]
}
// events.
const listeners: ?Object = options._parentListeners
for (const key in listeners) {
data[camelize(key)] = listeners[key]
}
return data
}
export default {
name: 'transition',
props: transitionProps,
abstract: true, // 抽象组件,意思是不会真实渲染成dom,辅助开发
render (h: Function) {
// 通过slots获取到真实渲染元素children
let children: any = this.$slots.default
const mode: string = this.mode
const rawChild: VNode = children[0]
// 添加唯一key
// compon