资讯专栏INFORMATION COLUMN

【翻译】javascript 动画原理浅析

Worktile / 1265人阅读

摘要:动画原文通常,框架会为你处理动画。整个的动画过程被分成了很小的步骤,每一个步骤被定时器调用。因为定时器的周期非常短,所以动画看起来是连续的。已经过去的动画时间作为分子,计算每一帧通过公式。线性的使动画以固定的速度进行。

动画

原文:http://javascript.info/tutori...

通常,框架会为你处理动画。但是,你可能想知道仅仅用javascript怎么来实现动画,和可能出现的一些问题。理解这项技术对于创建复杂的动画是很有帮助的,即使在框架的帮助下。

动画基础

javascript的动画是通过周期性的改变DOM元素的style 或者 canvas 的对象。
整个的动画过程被分成了很小的步骤(step),每一个步骤被定时器调用。因为定时器的周期非常短,所以动画看起来是连续的。
伪代码:

var id = setInterval(function(){
    /*当前显示帧*/
    if(/*完成*/) clearInterval(id)
}, 10)

上面代码每一帧的间隔是 10ms ,意味着每秒钟有100帧。
在大多数的javascript框架中 10-15ms的delay是默认的。越短的延时让动画看起来更加流畅,但是只有在浏览器足够快的时候,每一步的动画才会准时运行。
如果动画需要许多计算,CPU可能会100%的负载,动画就会变得迟缓。这种情况下,delay就应该被增加。例如,delay 40ms 就是每秒25帧,接近24帧的电影标准。


setInterval 而不是用 setTimout(其实在现代浏览器中大多使用requestAnimationFrame)

我们使用setInterval,而不是递归的使用setTimeout,是因为我们想要一帧一个delay,而不是所有帧之间有一个固定的delay.查阅Understanding timers: setTimeout and setInterval来了解setInterval和递归的setTimeout之间到底有什么不同。(译者:经测试最新的chrome > 56中,setInterval的行为跟本文中描述的不同。当函数执行时间超过了delay时间,下一个函数不会马上运行,仍然会等一个delay的间隔,再执行。但本文仍有参考价值)

例子

例如,一个元素移动通过改变element.stye.left从0到100px。每10ms改变1px。







在新的窗口打开

动画重构

为了让动画更加通用,我们介绍下面的一些参数:

delay
  每一帧之间的间隔(ms).例如,10ms

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
这个算法完全遵循上面的描述

Example

让我们基于这个来创建一个移动的动画

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。
用法:

数学,进度增量函数

动画就是是根据给定的规则,一直改变属性。在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来实现。
未完待续
第一次翻译点东西,质量不好,主要是做记录用。里面有很多比较"术语"的,不好翻,最好看原文。后面我也会继续修改。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81899.html

相关文章

  • 2017-09-01 前端日报

    摘要:前端日报精选如何优雅的编写代码深入理解内部机制专题之函数组合年月个有趣的和库最经典的前端面试题之一,你能答出什么幺蛾子中文翻译深入理解响应式原理掘金译与和交互掘金箭头函数使用禁忌技术栈耕耘助力美团点评前端进阶之路前端模块 2017-09-01 前端日报 精选 如何优雅的编写 JavaScript 代码深入理解 Node.js Stream 内部机制JavaScript专题之函数组合20...

    Arno 评论0 收藏0
  • 原理解释 - 收藏集 - 掘金

    摘要:巧前端基础进阶全方位解读前端掘金我们在学习的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。 计算机程序的思维逻辑 (83) - 并发总结 - 掘金从65节到82节,我们用了18篇文章讨论并发,本节进行简要总结。 多线程开发有两个核心问题,一个是竞争,另一个是协作。竞争会出现线程安全问题,所以,本...

    AlphaGooo 评论0 收藏0
  • 原理解释 - 收藏集 - 掘金

    摘要:巧前端基础进阶全方位解读前端掘金我们在学习的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。 计算机程序的思维逻辑 (83) - 并发总结 - 掘金从65节到82节,我们用了18篇文章讨论并发,本节进行简要总结。 多线程开发有两个核心问题,一个是竞争,另一个是协作。竞争会出现线程安全问题,所以,本...

    forrest23 评论0 收藏0
  • 2017-10-16 前端日报

    摘要:前端日报精选译发布了王跃关于微信小程序的技术,也许你想错了细说中的浏览器页面渲染工作原理浅析腾讯前端团队社区中文第期安息吧,长存译借助函数完成可组合的数据类型软件编写第十部分掘金对象与原型掘金技术周刊期知乎专栏是真正的语言 2017-10-16 前端日报 精选 [译]Vue 2.5 发布了王跃:关于微信小程序的技术,也许你想错了细说Web API中的Blobchrome浏览器页面渲染工...

    molyzzx 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<