摘要:傳送門從到,開發一個動畫庫上一節講到了最基礎的內容,為動畫構建幀值對應的函數關系,完成由幀到值的計算過程。這一節將在上節代碼的基礎上談談如何給一個完整的動畫添加各類事件。
傳送門:從0到1,開發一個動畫庫(1)
上一節講到了最基礎的內容,為動畫構建“幀-值”對應的函數關系,完成“由幀到值”的計算過程。這一節將在上節代碼的基礎上談談如何給一個完整的動畫添加各類事件。
在添加各類事件之前,我們先對_loop循環函數進行一些改進:
_loop() { const t = Date.now() - this.beginTime, d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; if (this.state === "end" || t >= d) { this._end(); } else if (this.state === "stop") { this._stop(t); } else if (this.state === "init") { this._reset(); } else { this._renderFunction(t, d, func) window.requestAnimationFrame(this._loop.bind(this)); } }
可以清晰地看到,我們在循環中增加了很多類型的判斷,根據state當前不同的狀態執行相應的處理函數:我們新增了_end、_stop、_reset方法分別去處理結束、暫停和重置這三種狀態,接下來我們依次講解這些狀態的處理。
End我們在Core類下增加_end、end和renderEndState方法,end方法用于主動結束動畫:
end() { this.state === "play" ? (this.state = "end") : this._end(); } _end() { this.state = "end"; this._renderEndState(); this.onEnd && this.onEnd(); } _renderEndState() { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(d, d, func); }
通過執行end方法,我們可以主動結束動畫:如果當前目標處于運動狀態,則將其設置為end,因此下一個_loop函數被執行的時候,程序就被流向了_end處理函數;若為其他狀態,意味著循環沒有被打開,我們就直接調用_end方法,使其直接到終止狀態。
_end函數的作用有三個:
將當前狀態設置為end(為何要重復設置一次狀態呢?這不是多余的嗎?其實,倘若我們主動觸發end去結束動畫,這的確是多余的,但如果是動畫自己進行到了末尾,也就是t >= d的時刻,則必須得在_end中去設置狀態,以確保它處于結束狀態)
通過_renderEndState方法,將目標變成結束狀態
若有回調函數則執行回調
Reset重置動畫的方式也是大同小異,與上面一樣
reset() { this.state === "play" ? (this.state = "init") : this._reset(); } _reset() { this.state = "init"; this._renderInitState(); this.onReset && this.onReset(); } _renderInitState() { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(0, d, func); }Stop
讓動畫暫停也是與上述兩者一樣,但唯一的區別是,需要給_renderStopState方法傳入當前時間進度:
stop() { if (this.state === "play") { this.state = "stop"; } else { // 使目標暫停,無需像end或reset那樣將目標變成結束/起始狀態,保持當前狀態即可 this.state = "stop"; this.onStop && this.onStop(); } } _stop(t) { this.state = "stop"; this._renderStopState(t); this.onStop && this.onStop(); } _renderStopState(t) { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(t, d, func); }play
我們要在動畫開始執行的時候觸發onPlay事件,只需在_play方法內增加一行代碼即可:
_play() { this.state = "play"; // 新增部分 this.onPlay && this.onPlay(); this.beginTime = Date.now(); const loop = this._loop.bind(this); window.requestAnimationFrame(loop); }```
完整代碼如下:
import Tween from "./tween"; class Core { constructor(opt) { this._init(opt); this.state = "init"; } _init(opt) { this._initValue(opt.value); this.duration = opt.duration || 1000; this.timingFunction = opt.timingFunction || "linear"; this.renderFunction = opt.render || this._defaultFunc; /* Events */ this.onPlay = opt.onPlay; this.onEnd = opt.onEnd; this.onStop = opt.onStop; this.onReset = opt.onReset; } _initValue(value) { this.value = []; value.forEach(item => { this.value.push({ start: parseFloat(item[0]), end: parseFloat(item[1]), }); }) } _loop() { const t = Date.now() - this.beginTime, d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; if (this.state === "end" || t >= d) { this._end(); } else if (this.state === "stop") { this._stop(t); } else if (this.state === "init") { this._reset(); } else { this._renderFunction(t, d, func) window.requestAnimationFrame(this._loop.bind(this)); } } _renderFunction(t, d, func) { const values = this.value.map(value => func(t, value.start, value.end - value.start, d)); this.renderFunction.apply(this, values); } _renderEndState() { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(d, d, func); } _renderInitState() { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(0, d, func); } _renderStopState(t) { const d = this.duration, func = Tween[this.timingFunction] || Tween["linear"]; this._renderFunction(t, d, func); } _stop(t) { this.state = "stop"; this._renderStopState(t); this.onStop && this.onStop(); } _play() { this.state = "play"; this.onPlay && this.onPlay(); this.beginTime = Date.now(); const loop = this._loop.bind(this); window.requestAnimationFrame(loop); } _end() { this.state = "end"; this._renderEndState(); this.onEnd && this.onEnd.call(this); } _reset() { this.state = "init"; this._renderInitState(); this.onReset && this.onReset(); } play() { this._play(); } end() { this.state === "play" ? (this.state = "end") : this._end(); } reset() { this.state === "play" ? (this.state = "init") : this._reset(); } stop() { if (this.state === "play") { this.state = "stop"; } else { this.state = "stop"; this.onStop && this.onStop(); } } } window.Timeline = Core;
相應地,html的代碼也更新如下,添加了各類按鈕,主動觸發目標的各類事件:
看到這里,我們第二節的內容就結束啦,下一節,我們將介紹:
支持自定義路徑動畫
動畫間的鏈式調用
下一節再見啦^_^
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/92829.html
摘要:傳送門從到,開發一個動畫庫如今市面上關于動畫的開源庫多得數不勝數,有關于甚至是渲染的,百花齊放,效果炫酷。當你看到的時候可能不大明白外界傳入的到底是啥其實是一個數組,它的每一個元素都保存著獨立動畫的起始與結束兩種狀態。 傳送門:從0到1,開發一個動畫庫(2) 如今市面上關于動畫的開源庫多得數不勝數,有關于CSS、js甚至是canvas渲染的,百花齊放,效果炫酷。但你是否曾想過,自己親手...
摘要:傳送門從到,開發一個動畫庫如今市面上關于動畫的開源庫多得數不勝數,有關于甚至是渲染的,百花齊放,效果炫酷。當你看到的時候可能不大明白外界傳入的到底是啥其實是一個數組,它的每一個元素都保存著獨立動畫的起始與結束兩種狀態。 傳送門:從0到1,開發一個動畫庫(2) 如今市面上關于動畫的開源庫多得數不勝數,有關于CSS、js甚至是canvas渲染的,百花齊放,效果炫酷。但你是否曾想過,自己親手...
摘要:幸運的是,供應似乎與需求相匹配,并且有多種選擇。讓我們來看看年值得關注的十大動畫庫。八年了,仍然是一個強大的動畫工具。接下來在這個令人驚嘆的動畫庫列表上的就是了。,通常被稱為動畫平臺,我們忽略它在列表中的排名,它是列表中最受歡迎的庫之一。 原文鏈接原譯文鏈接 現代網站的客戶端依靠高質量的動畫,這就使得JavaScript動畫庫的需求不斷增加。幸運的是,供應似乎與需求相匹配,并且有多種選...
閱讀 711·2021-11-18 10:02
閱讀 3590·2021-09-02 10:21
閱讀 1750·2021-08-27 16:16
閱讀 2063·2019-08-30 15:56
閱讀 2390·2019-08-29 16:53
閱讀 1376·2019-08-29 11:18
閱讀 2960·2019-08-26 10:33
閱讀 2647·2019-08-23 18:34