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');
}
复制代码
好像没少多少???
嗯,心智负担降了一丢丢.多少仍是方便一点了吧...
溜了溜了...