因为每一个插手 Timeline 行列的 Animation 动画都大概有纷歧样的 delay,也就是说有纷歧样的开始动画的时间。所以我们需要在 Timeline 类中的 constructor 下成立一个 START_TIMES 存储空间,把我们所有 Animation 对应的开始时间都存储起来。
export class Animation { constructor(object, property, startValue, endValue, duration, delay, timingFunction) { this.object = object; this.property = property; this.startValue = startValue; this.endValue = endValue; this.duration = duration; this.timingFunction = timingFunction; this.delay = delay; } run(time) { console.log(time); let range = this.endValue - this.startValue; this.object[this.property] = this.startValue + (range * time) / this.duration; } }
然后在 Timeline 插手动画的 add 要领中,把动画的开始时间插手到 START_TIMES 数据内里。假如利用者没有给 add 要领传入 startTime 参数,那么我们需要给它一个默认值为 Date.now() 。
add(animation, startTime) { if (arguments.length < 2) startTime = Date.now(); this[ANIMATIONS].add(animation); this[START_TIMES].set(animation, startTime); }
接下来我们就可以去改革开始时间的逻辑:
第一种环境: 假如我们动画的开始时间是小于,Timeline 的开始时间的,那么我们当前动画的时间进度就是 当前时间 - Timeline 开始时间
第二种环境: 动画的开始时间大于 Timeline 的开始时间,那么当前动画的时间进度就是 当前时间 - 动画的开始时间
代码实现如下:
start() { let startTime = Date.now(); this[TICK] = () => { let now = Date.now(); for (let animation of this[ANIMATIONS]) { let t; if (this[START_TIMES].get(animation) < startTime) { t = now - startTime; } else { t = now - this[START_TIMES].get(animation); } if (t > animation.duration) { this[ANIMATIONS].delete(animation); t = animation.duration; } animation.run(t); } requestAnimationFrame(this[TICK]); }; this[TICK](); }
这样 Timline 就支持随时给它插手一个 animation 动画。为了利便我们测试这个新的成果,我们把 tl 和 animation 都挂载在 window 上。
这里我们就窜改一下 main.js 中的代码:
start() { let startTime = Date.now(); this[TICK] = () => { let now = Date.now(); for (let animation of this[ANIMATIONS]) { let t; if (this[START_TIMES].get(animation) < startTime) { t = now - startTime; } else { t = now - this[START_TIMES].get(animation); } if (t > animation.duration) { this[ANIMATIONS].delete(animation); t = animation.duration; } animation.run(t); } requestAnimationFrame(this[TICK]); }; this[TICK](); }
我们从头 webpack 打包后,就可以在 console 内里执行以下呼吁来给 Timeline 插手一个动画:
tl.add(animation);
好,这个就是 Timeline 更新的设计。可是写到这里,我们其实还没有去让 delay 这个参数的值去让动画被延迟。
其实这里无非就在 t 的计较中,最后减去 animation.delay 即可。
if (this[START_TIMES].get(animation) < startTime) { t = now - startTime - animation.delay; } else { t = now - this[START_TIMES].get(animation) - animation.delay; }
可是我们需要留意一种非凡环境,假如我们 t - 延迟时间 得出的时间是小于 0 的话,那么代表我们的动画还没有达到需要执行的时间,只有 t > 0 才需要执动作画。所以最后在执动作画的逻辑上,插手一个判定。
if (t > 0) animation.run(t);
那么接下来我们来实验实现它的 pause(暂停) 和 resume(规复) 的本领。
实现暂停和重启成果首先我们来实验插手暂停的成果。
实现 Pause要给 Timeline 实现 Pause 的本领,首先我们就要把 tick 给 cancel 掉。也就是让我们 Timline 的时间遏制,假如一个钟可妙手表的秒针不再动了,那么时间自然就遏制了。
要打消掉 tick ,首先我们要知道触发的这个 tick 在运作的是什么。毋庸置疑,就是我们的 requestAnimationFrame。
还记得我们一开始声明的 TICK_HANDLER 吗?这个常量就是用来存储我们当前 tick 的事件的。
所以第一步就是用 TICK_HANDLER 来储存我们的 requestAnimationFrame。tick 的启动是在我们 Timeline 类中的 start 要领中启动的,所以这里我们需要窜改 start 要领中的 requestAnimationFrame:
start() { let startTime = Date.now(); this[TICK] = () => { let now = Date.now(); for (let animation of this[ANIMATIONS]) { let t; if (this[START_TIMES].get(animation) < startTime) { t = now - startTime - animation.delay; } else { t = now - this[START_TIMES].get(animation) - animation.delay; } if (t > animation.duration) { this[ANIMATIONS].delete(animation); t = animation.duration; } if (t > 0) animation.run(t); } this[TICK_HANDLER] = requestAnimationFrame(this[TICK]); }; this[TICK](); }
然后我们在 pause() 要领中挪用以下 cancelAnimationFrame 。
pause() { cancelAnimationFrame(this[TICK_HANDLER]); }
Pause(暂停) 照旧较量简朴的,可是 resume(重启)就较量巨大了。
实现 Resume那么实现 resume 的第一步一定就是从头启动 tick。可是 tick 中的 t(动画开始时间)必定是差池的,所以我们要想步伐去处理惩罚 pause 傍边的逻辑。
在实现 Resume 之前,我们需要弄一点 DOM 的对象来测试它。所以我们先成立一个新的 HTML,在内里成立一个 div 元素。