原文:http://javascript.info/tutori...javascript
一般,框架会为你处理动画。可是,你可能想知道仅仅用javascript怎么来实现动画,和可能出现的一些问题。理解这项技术对于建立复杂的动画是颇有帮助的,即便在框架的帮助下。css
javascript的动画是经过周期性的改变DOM元素的style 或者 canvas 的对象。
整个的动画过程被分红了很小的步骤(step),每个步骤被定时器调用。由于定时器的周期很是短,因此动画看起来是连续的。
伪代码:html
var id = setInterval(function(){ /*当前显示帧*/ if(/*完成*/) clearInterval(id) }, 10)
上面代码每一帧的间隔是 10ms ,意味着每秒钟有100帧。
在大多数的javascript框架中 10-15ms的delay是默认的。越短的延时让动画看起来更加流畅,可是只有在浏览器足够快的时候,每一步的动画才会准时运行。
若是动画须要许多计算,CPU可能会100%的负载,动画就会变得迟缓。这种状况下,delay就应该被增长。例如,delay 40ms 就是每秒25帧,接近24帧的电影标准。java
setInterval
而不是用setTimout
(其实在现代浏览器中大多使用requestAnimationFrame)咱们使用setInterval,而不是递归的使用setTimeout,是由于咱们想要一帧一个delay,而不是全部帧之间有一个固定的delay.查阅Understanding timers: setTimeout and setInterval来了解setInterval和递归的setTimeout之间到底有什么不一样。(译者:经测试最新的chrome > 56中,setInterval的行为跟本文中描述的不一样。当函数执行时间超过了delay时间,下一个函数不会立刻运行,仍然会等一个delay的间隔,再执行。但本文仍有参考价值)web
例如,一个元素移动经过改变element.stye.left从0到100px。每10ms改变1px。算法
<html> <head> <link type="text/css" rel="stylesheet" href="/files/tutorial/browser/animatio/animate.css"> <script> function move(elem) { var left = 0; function frame(){ left++ // 更新参数 elem.style.left = left + 'px' // 显示帧 if(left === 100) //检查结束条件 clearInterval(id) } var id = setInterval(frame, 10) //没10ms绘制一次 } </script> </head> <body> <div onclick="move(this.children[0])" class="example_path"> <div class="example_block"></div> </div> </body> </html>
在新的窗口打开chrome
为了让动画更加通用,咱们介绍下面的一些参数:编程
delay
每一帧之间的间隔(ms).例如,10mscanvas
duration
整个动画完成须要的时间(ms)。例如:1000ms浏览器
当动画开始的时候,咱们也能够用:
start 动画开始的时间,start = new Date
动画过程的核心,每一帧咱们须要计算:
timePassed
从动画开始所通过的时间(ms)。
从0到动画(duration)结束.可是偶尔可能会超过结束时间,由于浏览器的计时器并不许确。
progress
已通过去的动画时间做为分子,计算每一帧经过公式timePassed/duration
。值得范围一般是0到1。
例如,progress的值为0.5就是说动画时间(duration)已通过去了一半。
delta(progress)
一个返回当前动画进度的函数。
例如,咱们让高度属性从0%变化到100%。
咱们可让动画均匀的显示,这样动画进度看起来就是线性的。
Mapping:
->progress = 0 -> height = 0%
->progress = 0.2 -> height = 20%
->progress = 0.5->height = 50%
->progress = 0.8 -> height = 80%
->progress = 1 -> height = 100%
可是咱们可能想让动画缓慢的开始而后再加速。这样的话通过一半的动画时间高度可能只有25%
,而后逐渐加速到100%。
Mapping:
->progress = 0 -> height = 0%
->progress = 0.2 -> height = 4%
->progress = 0.5->height = 25%
->progress = 0.8 -> height = 64%
->progress = 1 -> height = 100%
delta(progress) 是一个映射动画进度增量的函数,
动画进度一般是0到1之间的一个数字。
这篇文章会用一些例子进一步讨论几种增量函数
step(delta)
这个函数是实际上用来作这件事的函数。
它计算出增量的结果而且应用它。
对于这个高度的例子,他们多是:
function state(delta) { elem.style.height = 100*delta + "%" }
到如今为止几个重要的参数是:
-> delay是setInterval
的第二个参数。
-> duration是动画完成须要的时间。
-> progress是动画已经通过的时间,除以duration使它的值在0到1之间。
-> delta经过当前的时间,计算当前的动画进度。
-> step作了视觉上(?)的工做。它得到当前的动画进度,而且把它应用在元素上。
让咱们把上面讨论的简单的写成一个可扩展的动画核心。
下面的动画函数执行时间管理而且把工做分配给delta和step:
function animate(opts) { var start = new Date; var id = setInterval(function(){ var timePassed = new Date - start; var progress = timePassed / opts.duration; if(progress > 1) progress = 1; var delta = opts.delta(progress) opts.step(delta) if(progress == 1) { clearInterval(id) } }, opts.delay || 10) }
参数对象应该包含如下的一些动画属性:
-> delay
-> duration
-> function delta
-> function step
这个算法彻底遵循上面的描述
让咱们基于这个来建立一个移动的动画
function move(element, delta, duration){ var to = 500; animate({ delay: 10, duration: duration || 1000, delta: delta, step: function(delta){ element.style.left = to * delta + 'px' } }) }
它把工做指派给animate
,给animate传入了delay,用户提供的duration
, delta
, 和 step
。
delta = function(p) { return p}
意味着动画进程一直是均匀的
step
用一个简单的公式映射0..1,delta返回一个进度值 0..to。把这些结果应用到element.style.left。
用法:
<div onclick="move(this.children[0], function(p) {return p})" class="example_path"> <div class="example_block"></div> </div>
动画就是是根据给定的规则,一直改变属性。在javascript动画中,这个规则就是delta函数来实现的。
不一样的delta使动画的速度,加速度和其余的参数表现出各类各样的形式。
数学公式一般被用在这里。可是它们对于只作web编程和忘记学校里的数学的人来讲,可能很陌生。在这个章节,咱们将浏览不少的受欢迎的公式并看一下它们是如何工做的。
动画运动的例子,提供不一样的delta.
线性 delta
function linear(progress){ return progress; }
水平方向指的是时间进度,垂直方向指的是动画进程。
咱们已经能看见了。线性的delta使动画以固定的速度进行。
Power of n
也是一个简单的例子。delta是progress的n次方。例如2次,3次方等。
例如2次方
function quad(progress) { return Math.pow(progress, 2) }
增长加速度的影响。例如,下面这个图片是5次方。
Circ: 圆的一部分
function circ(progress) { return 1 - Math.sin(Math.acos(progress)) }
Back: the bow function
这个函数向弓同样工做:首先咱们"拉开工,而后发射出去"。
不像先前的函数,它会依赖于一个附加的参数x,这就是“弹性系数”。
它定义了“拉弓”的距离。
代码是:
function back(progress, x) { return Math.pow(progress, 2) * ((x + 1) * progress - x) }
图像x=1.5
bounce(弹跳)
想象一下咱们释放一个球,它掉在地上,而后弹跳几回,最后中止。
bounce事实上作了相反的事情。属性将会一直改变知道它达到目标点。
这个函数比以前要复杂一些,也没有简单的数学公式。
function bounce(progress) { for(var a = 0, b=1, result; 1; a+=b, b /= 2) { if (progress >= (7-4*a) / 11){ return -Math.pow(11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2) } } }
*这段代码曲子 MooTools.FX.Transitions.据我所知,仍然有其余实现bounce的算法。
Elastic 弹性
这个函数也依赖于额外的参数x,x定义了初始范围。
function elastic(progress, x){ return Math.pow(2, 10 * (progress - 1)) * Math.cos(20 * Math.PI * x/3 *progress) }
图像 x=1.5
在这个例子中,为了让动画更加平滑,时间是2s.
反向函数(Reverse functions)
一个javascript框架常常会提供的一种delta函数。
它们的直接使用被称做 easeIn.
偶尔的时候,须要以时间倒退的方式来展现动画。这就叫作easeout 被'time-reversing'delta来实现。
未完待续
第一次翻译点东西,质量很差,主要是作记录用。里面有不少比较'术语'的,很差翻,最好看原文。后面我也会继续修改。