JavaScript動畫實現初探

jopen 9年前發布 | 41K 次閱讀 JavaScript開發 JavaScript

前言

現如今,許多頁面上均有一些動畫效果。適當的動畫效果可以在一定程度上提高頁面的美觀度,具有提示效果的動畫可以增強頁面的易用性。

實現頁面動畫的途徑有兩種。一種是通過操作JavaScript間接操作CSS樣式,每隔一段時間更新一次;一種是直接通過CSS定義動畫。第二種方法在CSS3成熟之后被廣泛采用。在本文中,我們討論第一種方法的原理和實現。

JavaScript動畫實現原理
首先我們需要知道兩個重要的概念,動畫時間進程和動畫效果進程。

動畫時間進程指從時間上看動畫的完成度,是一個[0, 1]之間的數字。假設動畫于時間戳t1開始,要在t2結束,當前時間戳為t,那么該動畫目前的時間進程為(t-t1)/(t2-t1)。如果你不能理解,我建議你用紙筆畫出來。理解這一概念對理解本文至關重要。

動畫效果進程指被動畫的屬性值當前的增量。假設我們要將#el元素的CSS left 屬性從100px變到200px,當前已經變到了130px,那么該動畫目前的效果進程為130px - 100px = 30px。

假設動畫時間進程和動畫效果進程都是線性的。那么如果知道了動畫時間進程,一定可以得到動畫效果進程。


JavaScript動畫實現初探

根據這個解釋,我們很快可以編寫出一個線性的動畫。
(function() {
      var begin, // 開始動畫的時間
        el, start, end, duration; 
      var INTERVAL = 13;

  function now() {
    return (new Date).getTime();
  }

  /**
   * 執行一步動畫(更新屬性)
   */
  function _animLeft() {
    var pos = (now() - begin) / duration;
    if (pos >= 1.0) {
      return false;
    }
    return !!(el.style.left = start + (end - start) * pos);
  }

  /**
   * 對一個DOM執行動畫,left從_start到_end,執行時間為
   * _duration毫秒。
   * 
   * @param  {object} _el       要執行動畫的DOM節點
   * @param  {integer} _start   left的起始值
   * @param  {integer} _end     left的最終值
   * @param  {integer} _duration  動畫執行時間
   */
  function animLeft(_el, _start, _end, _duration) {
    stopped = false;
    begin = now();
    el = _el;
    start = _start;
    end = _end;
    duration = _duration || 1000;

    var step = function() {
      if (_animLeft()) {
        setTimeout(step, INTERVAL);
      }
    };
    setTimeout(step, 0);
  }

  window.animLeft = animLeft;
})();

animLeft(
  document.getElementById('el'),
  100,
  200
)</pre><br />


JSBin
easing

很多時候,我們需要的動畫并非線性的。所謂非線性,從直觀上看,就是動畫速度隨著時間會產生變化。那么如何實現變速的動畫呢?

由前所述,我們知道通過控制動畫的時間進程就相當于控制動畫的效果進程。隨著真實世界的時間進程推移,動畫的時間進程跟著推移,從而控制動畫的效果進程推移。那么,我們可以通過修改真實世界的時間進程和動畫的時間進程間的映射關系,從而控制動畫進程。如果你感到困惑,沒關系,請看下圖:


JavaScript動畫實現初探

這是線性動畫中,真實世界的時間進程和動畫進程的映射關系。接下來,我們將其進行變換。


JavaScript動畫實現初探

這條曲線實際上是函數y = x * x的圖像。可以看到,兩個曲線的定義域和值域并沒有變化。曲線的斜率就是動畫的速率。接下來我們將兩張圖重疊在一起做一個對比。


JavaScript動畫實現初探

在真實世界的時間進行到x0的時候,動畫進程原本應該進行到y0,在進行變換之后,只進行到y1。到最后,百川歸海,兩條線交匯于點(1, 1)。這里,y = x x是變換函數(easing function)。

我們修改一下上面的例子,讓動畫變成非線性的。
function ease(time) {
      return time  time;
    }

/**
 * 執行一步動畫(更新屬性)
 */
function _animLeft() {
  var pos = (now() - begin) / duration;
  if (pos >= 1.0) {
    return false;
  }
  pos = ease(pos);
  return !!(el.style.left = (start + (end - start) * pos) + "px");
}</pre><br />


JSBin

我們可以在jQuery的代碼中看到這樣的函數。

jQuery.easing = {
      linear: function( p ) {
        return p;
      },
      swing: function( p ) {
        return 0.5 - Math.cos( p * Math.PI ) / 2;
      }
    };


因此,你可以往jQuery.easing里面添加easing function,使得jQuery支持新的動畫速率控制方法。注意,easing function的定義域和值域必須都為[0, 1]。

 jQuery.easing.myEasing = function( p ) { return ... }

總結

本文介紹了JavaScript動畫的最基本的原理。

JavaScript動畫實質上也是通過操作CSS去執行動畫。動畫的時間進程可以決定動畫的效果進程。通過操作真實世界的時間進程和動畫的時間進程之間的關系,我們可以將線性動畫變換成非線性的動畫。

本文來自: 子回的博客

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!