簡化Tween的用法

先放場景示例

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');
}
複製代碼

好像沒少多少???

嗯,心智負擔降了一丟丟.多少仍是方便一點了吧...

溜了溜了...

相關文章
相關標籤/搜索