使用JavaScript 实现时间轴与动画效果的示例代码(8)

<!-- animation.html --> <body> <div></div> <button>Pause</button> <button>Resume</button> <script src="https://www.jb51.net/article/main.js"></script> </body>

// animation-demo.js 中加入 resume 按钮事件绑定。 document.querySelector('#resume-btn').addEventListener( 'click', () => tl.resume() );

根据我们上面讲到的逻辑,resume 最基本的理解,就是重新启动我们的 tick。那么我们就试试直接在 resume 方法中执行 this[TICK]() 会怎么样。

resume() { this[TICK](); }

使用JavaScript 实现时间轴与动画效果的示例代码

在动画中,我们可以看到,如果我们直接在 resume 中执行 tick 的话,重新开始动画的盒子,并没有在原来暂停的位置开始继续播放动画。而是跳到了后面。

很显然,在我们点击 resume 的时候,我们的动画并没有记住我们暂停时候的位置。所以在我们动画暂停的同时,我们需要把 暂停的开始时间和暂停时间给记录下来。

这两个变量因为是需要在 Animation 类中使用的,所以这里要把它们定义在全局作用域之中。那么我们就用 PAUSE_START 和 PAUSE_TIME 两个常量来保存他们。

const PAUSE_START = Symbol('pause-start'); const PAUSE_TIME = Symbol('pause-time');

接下来就是在我们暂停的时候记录一下当时的时间:

pause() { this[PAUSE_START] = Date.now(); cancelAnimationFrame(this[TICK_HANDLER]); }

其实我们记录暂停的开始时间是为了什么呢?就是为了在我们继续播放动画的时候,知道我们当下距离开始暂停的时候的时间相差了多久。

刚刚我们在动画里看到的现象是什么?就是我们重新启动 tick 的时候,动画的开始时间使用了当前的时间。这里说到的 “当前” 时间,就是 Timeline 已经跑到了哪里。显然这个开始时间是不正确的。

如果我们在暂停的时候,记录了那一刻的时间。然后在点击 resume 的时候计算暂停开始到点击 resume 时的时长。这样我们就可以用 tick 中的 t(动画开始时间)- 暂停时长 = 当前动画应该继续播放的时间。

使用这个算法,我们就可以让我们的动画,精确的在原来暂停的位置继续开始播放了。

接下来,我们来看看代码的逻辑怎么实现:

刚刚我们已将在暂停的时候加入到时间记录的逻辑里,接下来我们要记录一个暂停时长。在记录暂停时长之前,我们需要一个地方给这个值赋予一个初始值为 0 。

最好的地方就是在 Timeline 开始的时候就赋予这个默认值。我们的 PAUSE_TIME 有了初始值之后,我们在执行 resume 的时候,就可以用 Date.now() - PAUSE_START 就能得到暂停动画到现在的总时长。

这里有一个点,需要我们注意的。我们的动画可能会出现多次暂停,并且多次的续播。那么这样的话,如果我们每次都使用这个公式计算出新的暂停时长,然后覆盖 PAUSE_TIME 的值,其实是不正确的。

因为我们的 Timeline 一旦开启是不会停止的,时间一直都在流逝。如果我们每次都只是计算当前的暂停时长,回退的时间其实是不对的。而正确的方式是,每次暂停时都需要去叠加上一次暂停过的时长。这样最后回退的时间才是准确的。

所以我们赋值给 PAUSE_TIME 的时候是使用 +=,而不是覆盖赋值。

最后我们改造好的 Timeline 就是这样的:

export class Timeline { constructor() { this[ANIMATIONS] = new Set(); this[START_TIMES] = new Map(); } start() { let startTime = Date.now(); this[PAUSE_TIME] = 0; 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 - this[PAUSE_TIME]; } else { t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; } 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() { this[PAUSE_START] = Date.now(); cancelAnimationFrame(this[TICK_HANDLER]); } resume() { this[PAUSE_TIME] += Date.now() - this[PAUSE_START]; this[TICK](); } reset() {} add(animation, startTime) { if (arguments.length < 2) startTime = Date.now(); this[ANIMATIONS].add(animation); this[START_TIMES].set(animation, startTime); } }

我们运行一下代码看看是否正确:

使用JavaScript 实现时间轴与动画效果的示例代码

这样我们就完成了 Pause 和 Resume 两个功能了。

使用JavaScript 实现时间轴与动画效果的示例代码

这里我们就实现了一个可用的 Timeline 时间轴,下一篇文章我们重点去加强动画库的功能。

如果你是一个开发者,做一个个人博客也是你简历上的一个亮光点。而如果你有一个超级炫酷的博客,那就更加是亮上加亮了,简直就闪闪发光。

主题 Github 地址:https://github.com/auroral-ui/hexo-theme-aurora
主题使用文档:https://aurora.tridiamond.tech/zh/

使用JavaScript 实现时间轴与动画效果的示例代码

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsdgjx.html