【翻譯】javascript 動畫原理淺析

動畫

原文: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%
animation-linear-height

可是咱們可能想讓動畫緩慢的開始而後再加速。這樣的話通過一半的動畫時間高度可能只有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%
animation-quad-height

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

<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;
}

linear-delta

水平方向指的是時間進度,垂直方向指的是動畫進程。
咱們已經能看見了。線性的delta使動畫以固定的速度進行。
Power of n
也是一個簡單的例子。delta是progress的n次方。例如2次,3次方等。
例如2次方

function quad(progress) {
    return Math.pow(progress, 2)
}

quadrantic-function

增長加速度的影響。例如,下面這個圖片是5次方。

5-th degree

Circ: 圓的一部分

function circ(progress) {
    return 1 - Math.sin(Math.acos(progress))
}

a-piece-of-circle

Back: the bow function
這個函數向弓同樣工做:首先咱們"拉開工,而後發射出去"。
不像先前的函數,它會依賴於一個附加的參數x,這就是「彈性係數」。
它定義了「拉弓」的距離。
代碼是:

function back(progress, x) {
    return Math.pow(progress, 2) * ((x + 1) * progress - x)
}

圖像x=1.5
the-bow-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
Elastic-x-1.5

在這個例子中,爲了讓動畫更加平滑,時間是2s.
反向函數(Reverse functions)
一個javascript框架常常會提供的一種delta函數。
它們的直接使用被稱做 easeIn.
偶爾的時候,須要以時間倒退的方式來展現動畫。這就叫作easeout 被'time-reversing'delta來實現。
未完待續
第一次翻譯點東西,質量很差,主要是作記錄用。裏面有不少比較'術語'的,很差翻,最好看原文。後面我也會繼續修改。

相關文章
相關標籤/搜索