framesync.js 的设计思想

framesync.js 是什么?

约定:

  • 前一帧、当前帧、下一帧:指代 上一个 requestAnimationFrame,当前待执行的 requestAnimationFrame,与下一个 requestAnimationFrame。

framesync.js 是对浏览器原生 requestAnimationFrame 函数的封装,它作为 Popmotion.js 动画库的一部分,作为一个独立包分发。

它是一个很简单的库,它暴露出来的接口只有下面的几个:

js 复制代码
import sync,{flushSync,cancelSync,getFrameData} from "framesync"

这些接口本质上是一些提供订阅监听的对象:

  • sync:用来添加订阅
  • cancelSync:用来取消订阅
  • flushSync:用来发布订阅

这些对象都具有五个属性,分别是:read、update、preRender、render、postRender。它们的类型如下:

ts 复制代码
 type T = {
     read:function,
     update:function,
     preRender:function,
     render:function,
     postRender:function
 }

read、update、preRender、render、postRender,本质上是一组按顺序执行的生命周期函数,sync.read 先于 sync.update 之前执行。

这些接口实际上实现的是一个订阅发布模式,不过它与 requestAnimationFrame 关联到一起了,在当前帧的第一次调用 sync.xxx 进行订阅的时候,它会在 requestAnimationFrame 上添加发布函数的回调。由此,sync.xxx 的调用等同于 requestAnimationFrame 的调用。

目前为止,它解决了直接使用 requestAnimationFrame 的一个痛点(执行的顺序是按照其调用顺序,无法在中途插入一个优先级更高的监听),你可以通过在不同的生命周期中添加监听来处理优先级的问题,而无需关心调用顺序。

实际上,因为它也算是使用了适配器模式,sync.xxx 在内部对 requestAnimationFrame 是否存在进行了检测,如果不存在它会使用 setTimeout。也即,解决了 requestAnimationFrame 的另一个痛点:可能存在的兼容性问题。

原生的 requestAnimationFrame 如果要持续的运行下去需要使它自调用,一般会写一些类似下面的逻辑:

js 复制代码
requestAnimationFrame(() => {
    //一些逻辑
    requestAnimationFrame(//...)
})

这实在是麻烦,这种麻烦在 framesync.js 中也得到了封装:

js 复制代码
import sync,{flushSync,cancelSync,getFrameData} from "framesync"

sync.render(() => {
  //一些逻辑
},true)//注意这第二个参数,
//如果它是true,则回调会在每一帧都执行,直到使用cancelSync.render进行取消。

//如果你也希望手动控制,那么你其实也可以这样写
sync.render(() => {
  //这与上面的 requestAnimationFrame 自调用是类似的
  sync.render(//...)//为下一帧添加新订阅

这里再次解决一个痛点(命令式的自调用),将为自调用添加了声明式的语法糖。

另外,添加到 requestAnimationFrame 上的回调我有时候希望它立刻执行,这在 framesync.js中,则是 flushSync 的职责了,它本身就是发布函数,因此调用它就可以很方便的马上触发订阅。

js 复制代码
import sync,{flushSync,cancelSync,getFrameData} from "framesync"

sync.render(() => {})
//一些逻辑后
//希望马上调用sync.render(() => {})中的回调,你可以使用
flushSync.render()
//flushSync.xxx 实际上是一种"合并",
//假设你的代码是:sync.render(() => {},true),也就是它是自调用的,
//那么它本身的代码执行应该是:1 2 3 4 5 ...帧的执行
//如果你调用了 flushSync.xxx,那么 在你调用 flushSync.xxx 的那一帧里,
//(假设是一开始就调用了flushSync.xxx)代码执行会变为:12 3 4 5 ...帧的执行,
//也就是本来 1 2 用两帧处理的逻辑,合并到一帧去处理了。
//当然如果你的逻辑本身就是一帧解决的,那么flushSync.xxx 的作用就是马上处理这些逻辑(同步),
//而不是将其放到 requestAnimationFrame 的异步去处理。

一言以蔽之:flushSync.xxx的作用就是:合并加速。将两帧的逻辑合并到一帧去处理。

最后 getFrameData 是一个用于获取上一帧执行时间戳的函数,其类似于 requestAnimationFrame回调函数接收到的时间戳。

总结:

framesync.js解决了requestAnimationFrame的以下痛点:

  1. 执行的顺序是按照其调用顺序,无法在中途插入一个优先级更高的监听
  2. 可能存在的兼容性问题
  3. 命令式的自调用
  4. 无法立刻触发回调,压缩掉某一帧

有些东西用文字描述是困难的,作者能力有限只能够描述到这种程度,想要更深入的理解,还需要去阅读源码才行。

实际上库的源码并不多,但因为其大量使用了可变变量,可变变量在各函数间相互修改,导致逻辑理解困难。

但其核心其实就是一个订阅监听模式,只需要把握好"sync、cancelSync、flushSync"本质是用于订阅、取消订阅、发布的对象,那么在阅读源码的时候,就可以很轻易的找到主干,能够大大节约源码的理解时间。

相关推荐
QGC二次开发1 小时前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
A_aspectJ2 小时前
前端框架对比和选择
前端框架
多多*4 小时前
OJ在线评测系统 登录页面开发 前端后端联调实现全栈开发
linux·服务器·前端·ubuntu·docker·前端框架
罗_三金4 小时前
前端框架对比和选择?
javascript·前端框架·vue·react·angular
程序员小羊!5 小时前
前端框架对比和选择
前端框架
奶糖 肥晨1 天前
react是什么?
前端·react.js·前端框架
CyberMuse1 天前
ChatGPT 为何将前端框架从 Next.js 更换为 Remix以及框架的选择
前端框架
徊忆羽菲2 天前
学习使用在windows系统上安装vue前端框架以及环境配置图文教程
vue.js·学习·前端框架
WebGIS皮卡茂2 天前
【数据可视化】Arcgis api 4.x 专题图制作之分级色彩,采用自然间断法(使用simple-statistics JS数学统计库生成自然间断点)
javascript·arcgis·信息可视化·前端框架
图灵苹果2 天前
【个人博客hexo版】hexo安装时会出现的一些问题
前端·前端框架·npm·node.js