從0到1,開發一個動畫庫(1)

發表於2018-01-29

傳送門:從0到1,開發一個動畫庫(2)

如今市面上關於動畫的開源庫多得數不勝數,有關於CSS、js甚至是canvas渲染的,百花齊放,效果炫酷。但你是否曾想過,自己親手去實現(封裝)一個簡單的動畫庫?

本文將從零開始,講授如何搭建一個簡單的動畫庫,它將具備以下幾個特徵:

  • 從實際動畫中抽象出來,根據給定的動畫速度曲線,完成“由幀到值”的計算過程,而實際渲染則交給開發者決定,更具擴充性
  • 支援基本的事件監聽,如onPlayonStoponReset onEnd,及相應的回撥函式
  • 支援手動式觸發動畫的各種狀態,如playstopresetend
  • 支援自定義路徑動畫
  • 支援多組動畫的鏈式觸發

完整的專案在這裡:點贊行為高尚!,歡迎各種吐槽和指正^_^

OK,話不多說,現在正式開始。

作為開篇,本節將介紹的是最基本、最核心的步驟——構建“幀-值”對應的函式關係,完成“由幀到值”的計算過程。

目錄結構

首先介紹下我們的專案目錄結構:

/timeline是本專案的根目錄,各檔案的作用分別如下:

  • index.js 專案入口檔案
  • core.js 動畫核心檔案
  • easing.js 存放基本緩動函式

引入緩動函式

所謂動畫,簡單來說,就是在一段時間內不斷改變目標某些狀態的結果。這些狀態值在運動過程中,隨著時間不斷髮生變化,狀態值與時間存在一一對應的關係,這就是所謂的“幀-值”對應關係,常說的動畫緩動函式也是相同的道理。

有了這種函式關係,給定任意一個時間點,我們都能計算出對應的狀態值。OK,那如何在動畫中引入緩動函式呢?不說廢話,直接上程式碼:

首先我們在core.js中建立了一個Core類:

我們在建構函式中對例項呼叫_init函式,對其初始化:將傳入的引數儲存在例項屬性中。

當你看到_initValue的時候可能不大明白:外界傳入的value到底是啥?其實value是一個陣列,它的每一個元素都儲存著獨立動畫的起始與結束兩種狀態。這樣說好像有點亂,舉個例子好了:假設我們要建立一個動畫,讓頁面上的div同時往右、左分別平移300px、500px,此外還同時把自己放大1.5倍。在這個看似複雜的動畫過程中,其實可以拆解成三個獨立的動畫,每一動畫都有自己的起始與終止值:

  • 對於往右平移,就是把css屬性的translateX的0px變成了300px
  • 同理,往下平移,就是把tranlateY的0px變成500px
  • 放大1.5倍,也就是把`scale從1變成1.5

因此傳入的value應該長成這樣:[[0, 300], [0, 500], [1, 1.5]] 。我們將陣列的每一個元素依次儲存在例項的value屬性中。

此外,renderFunction是由外界提供的渲染函式,即opt.render,它的作用是:

動畫運動的每一幀,都會呼叫一次該函式,並把計算好的當前狀態值以引數形式傳入,有了當前狀態值,我們就可以自由地選擇渲染動畫的方式啦。

接下來我們給Core類新增一個迴圈函式:

_loop的作用是:倘若當前時間進度t還未到終點,則根據當前時間進度計算出目標現在的狀態值,並以引數的形式傳給即將呼叫的渲染函式,即renderFunction,並繼續迴圈。如果大於duration,則將目標的運動終止值傳給renderFunction,運動結束,將狀態設為end

程式碼中的Tween是從tween.js檔案引入的緩動函式,tween.js的程式碼如下(網上搜搜基本都差不多= =):

最後,給Core類增加play方法:

core.js的完整程式碼如下:

在html中引入它後就可以愉快地呼叫啦^ _ ^

PS:該專案是用webpack打包並以timeline.min.js作為輸出檔案,由於暫時沒用到index.js檔案,因此暫時以core.js作為打包入口啦~

看到這裡,本文就差不多結束了,下節將介紹如何在專案中加入各類事件監聽及觸發方式。

本系列文章將會繼續不定期更新,歡迎各位大大指正^_^

相關文章