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

上一篇文章《用 JSX 实现 Carousel 轮播组件》中,我们实现了一个 “基础” 的轮播组件。为什么我们叫它 “基础” 呢?因为其实它看起来已经可以满足我们轮播组件的功能,但是其实它还有很多缺陷我们是没有去完善的。

虽然我们已经在里面实现了两个功能,一是可以自动轮播,二是可以手势拖拽。但是其实它离一个真正意义上的可用程度还是有很远的距离的。

首先我们的自动轮播和拖拽是无法无缝连接的,也就是说当我们拖拽结束后,我们的轮播应该继续自动轮播的。这一点我们是还没有实现的。我们的拖拽本身也是有细节上的问题的,比方说它目前只支持鼠标的拖拽事件,并不支持触屏的拖拽,这个也是我们在网页研发过程中必须要去面对的问题。

第二我们动画是使用 CSS Animation 实现的,也不具备任何的自定义和相应变化的。

所以接下来我们来一起实现我们的动画库,但是实现动画库之前,我们需要拥有一个动画库中的时间轴库。这篇文章我们先来看看怎么去实现一个时间轴类,和一个基础的动画类来使用这个时间轴。

代码整理

首先我们发现之前写的 Carousel 组件的代码已经很复杂了,所以我们需要封装一下它,这里我们就把它独立放入一个 JavaScript 文件中。

在项目根目录,建立一个 carousel.js,然后把我们 main.js 中 carousel 组件相关的代码都移动到 carousel.js 中。

carousel.js 中只需要 import Component 即可,然后给我们的 Carousel 类加上 export。代码结构如下:

import { Component } from './framework.js'; export class Carousel extends Component {/** Carousel 里面的代码 */}

最后我们在 main.js 中重新 import Carousel 组件即可。

import { Component, createElement } from './framework.js'; import { Carousel } from './carousel.js'; let gallery = [ 'https://source.unsplash.com/Y8lCoTRgHPE/1600x900', 'https://source.unsplash.com/v7daTKlZzaw/1600x900', 'https://source.unsplash.com/DlkF4-dbCOU/1600x900', 'https://source.unsplash.com/8SQ6xjkxkCo/1600x900', ]; let a = <Carousel src=https://www.jb51.net/{gallery} />; // document.body.appendChild(a); a.mountTo(document.body);

整理好我们的代码,就可以开始写我们的时间轴库了。这个时间轴是我们动画库中的一部分,所以我们统一放入我们动画库的 JavaScript 文件中: animation.js。

我们是需要用这个时间轴去实现我们后续的动画库的,而动画中就有一个非常关键的概念,就是 “

最基础的动画能力,就是每帧执行了一个事件。

JavaScript 中的 “帧”

因为我们需要有“帧”才能实现我们的动画,所以我们需要先去了解 JavaScript 中的几种处理帧的方案。

人眼能够识别的动画的一个最高频率就是 60 帧

有的同学可能有看过李安导演的电影。比如,《比利·林恩的中场战事》就是全世界第一个 120 帧拍摄和 120 帧播放的电影。

也是因为帧率翻倍,所以很多地方就会感到很丝滑。但是一般我们的游戏,包括我们的显示器,它们支持的都是 60 帧。虽然我们看显示器的设置中,可能会有 70、80 帧,但是一般软件都会与 60 帧对其。

如果我们 1000 毫秒(一秒)里面需要 60 帧的话,那是多少毫秒是一帧呢?也就是 1000 / 60 = 16.666 1000 / 60 = 16.666 1000/60=16.666,所以 16 毫秒大概就是一帧的时间。

这个就是为什么我们一般都会用 16 毫秒作为一帧的时长。

实现“帧”的方法

接下来我们来分析一下,有哪些方法可以在 JavaScript 中实现 “帧”。

1. setInterval

第一种就是 setInterval,这个其实我们在写轮播图的时候就用过。让一个逻辑在每一帧中执行,就是这样的:

setInterval(() => {/** 一帧要发生的事情 */}, 16)

这里设置的时间隔,就是 16 毫秒,一帧的时长。

2. setTimeout

我们也是可以使用 setTimeout 这个去重复处理一帧中的事件。但是因为 setTimeout 是只执行一次的。所以我们需要给它一个函数名,方便我们后面重复调用它。

一般这种用来作为动画中的一帧的 setTimeout,都会命名为 tick。因为 tick 在英文中,就是我们时钟秒针走了一秒时发出来的声音,后面也用这个声音作为一个单词,来表达走了一帧/一秒。

我们的使用方式就是定义一个 tick 函数,让它执行一个逻辑/事件。然后使用 setTimeout 来加入一个延迟 16 毫秒后再执行一次自己。

let tick = () => { /** 我们的逻辑/事件 */ setTimout(tick, 16); }

3. requestAnimationFrame

最后现代浏览器支持了一个 requrestAnimationFrame(也叫 RAF)。这是在写动画时比较常用,它不需要去定义一帧的时长。

当我们申请浏览器执行下一帧的时候,就会执行传入 RAF 的 callback 函数。并且这个函数执行的时间是与浏览器的帧率是相关的。

所以,如果我们要做一些浏览器的降帧、降频的操作时,那么 RAF 就可以跟着浏览的帧率一起下降。

使用也是非常简单:

let tick = () => { /** 我们的逻辑/事件 */ setTimout(tick, 16); }

所以,一般最常用的就是这三种方案。如果我们的用户大部分都是使用现代浏览器的话,就推荐使用 requestAnimationFrame。

“为什么不用 setInterval 呢”?因为 setInterval 比较不可控,浏览器到底会不会按照我们设置的 16 毫秒去执行呢?这个就不好说了。

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

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