const manager = new TweenManager({duration, start, end, easing})
while(manager.next()) {
await TweenManager.frame()
const value = manager.currentValue
// do something
}
複製代碼
寫js動畫是件挺麻煩的事.須要緩動函數,須要定時器,遞歸的話須要考慮邊界條件.javascript
並且緩動函數用起來也很彆扭,參數多,又難記.t,b,c,d每次用都得去看下注釋.java
最近用到一個瀏覽器的API,createTreeWalker
,這個方法返回一個TreeWalker
對象.能夠用來遍歷dom樹.瀏覽器
用法以下dom
while(tree.nextNode()){
const dom = tree.currentNode
// dosomething
}
複製代碼
一時間思惟發散.若是按照這種設計方式來搞一個緩動函數的管理類,應該能夠簡化Tween的使用方式,省的每次去記API.async
實現上一節提到的還不夠.寫動畫麻煩還在於requestAnimationFrame
這個API用起來也是挺麻煩的.須要遞歸調用.函數
好比一個回到頂部的動畫動畫
function backTop(duration) {
const doc = document.body.scrollTop ? document.body : document.documentElement;
const start = doc.scrollTop;
const stamp = Date.now()
function step() {
let currentStep = Date.now() - stamp
if (currentStep >= duration) {
doc.scrollTop = 0
return
}
doc.scrollTop = Tween.Linear(currentStep, start, -start, duration)
requestAnimationFrame(step)
}
step()
}
複製代碼
寫法是比較繁瑣的.仔細考慮,其實遞歸只是指望定時執行而已.若是是其餘語言.一個sleep就搞定了.ui
JavaScript
雖然沒有sleep,但有了async/await
,咱們也能夠實現一個.this
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
(async () => {
console.log('1');
await sleep(1000);
console.log('2');
})();
複製代碼
而requestAnimationFrame
其實能夠與sleep(16)
互相代替,那咱們能夠實現一個frame
方法.spa
const frame = () => requestAnimationFrame ? new Promise(requestAnimationFrame) : sleep(16);
(async () => {
await frame();
console.log('1 frame');
})();
複製代碼
咱們指望的用法肯定了.接下來實現.其實代碼很少.
const Tween = {
Linear: function (t, b, c, d) {
return c * t / d + b;
},
}
const dftOption = {
duration: 300,
start: 0,
end: 0,
easing: Tween.Linear,
}
class TweenManager {
get distance() {
return this.$options.end - this.$options.start
}
get now() {
return Date.now ? Date.now() : new Date().getTime()
}
get currentStep() {
return this.now - this.stamp
}
get currentValue() {
const {distance, currentStep} = this
const {duration, easing, start} = this.$options
return easing(currentStep, start, distance, duration)
}
constructor(opt = {}) {
this.$options = {...dftOption, ...opt}
this.stamp = this.now
}
next() {
return this.$options.duration > this.currentStep
}
static sleep(time = 0) {
return new Promise(resolve => setTimeout(resolve, time))
}
static frame() {
return requestAnimationFrame ? new Promise(requestAnimationFrame) : TweenManager.sleep(16)
}
}
複製代碼
一樣是回到頂部的動畫,能夠簡化成這樣.
function backTop(duration) {
const doc = document.body.scrollTop ? document.body : document.documentElement;
const start = doc.scrollTop;
const manager = new TweenManager({duration, start, end: 0, easing: Tween.Linear});
while (manager.next()) {
await TweenManager.frame();
doc.scrollTop = manager.currentValue;
}
console.log('done');
}
複製代碼
好像沒少多少???
嗯,心智負擔降了一丟丟.多少仍是方便一點了吧...
溜了溜了...