timesnap技术拆解

一些备注

两次截屏之间的间隙时间似乎可设置 addAnimationGapThreshold;不对,只针对动画?

核心:p = timeHandler.goToTimeAndAnimateForCapture(browserFrames, marker.time);

关键:改写浏览器时间函数、控制时间线

思考:时间线能倒退么?不能,注释明确写了不能

需要了解browserFrames这个对象,puppeteer 的 Frame 对象?

初始化 marker 数组

循环 marker

进入某个 marker

根据 marker.time,进入该时间点

转换为虚拟时间_timesnap_processUntilTime(ms)

_pendingBlocks 是为了阻塞?中间不做处理?

block的概念:

1
2
3
// a block is a segment of blocking code, wrapped in a function
// to be run at a certain virtual time. They're created by
// window.requestAnimationFrame, window.setTimeout, and window.setInterval

感觉有点像是 JS 执行栈的概念,也是先进后出、后进先出。

执行期间,会不停往_pendingBlocks 中添加 block,因此每次执行完一个 block,都需要进行_sortPendingBlocks();

还是没找到它怎么放慢的、放多慢?

到底是 timesnap 放慢的还是 puppeteer 放慢的?读下 timesnap 文档

不是放慢,是阻塞!截图有多快,就看你的 CPU 有多强,这基本上就是各种文件操作和计算操作!

如何做到每一帧都执行截图操作的:_runAnimationFrames

如何插入_animationFrameBlocks 的:通过_requestAnimationFrame 插入的

goToTimeAndAnimateForCapture 返回的是个 Promise 实例,在这个实例的 then 方法中,执行截屏操作。

timesnap 通过 puppeteer 的 page.evaluateOnNewDocument()方法,给无头浏览器注入了一段立即执行函数,改写了诸如 requestAnimationFrame()等函数。

这是在页面一开始就执行的。

常见问题

为什么前端报错,就会导致截屏终止?

在 timesnap/lib/overwrite-time.js 中,有如下这个函数,用于执行指定的某个时间点的操作;其核心是通过 puppeteer 的frame.evaluate函数给浏览器注入自定义的 JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const goToTimeAndAnimate = function (browserFrames, time) {
// Goes to a certain time. Can't go backwards
return Promise.all(
browserFrames.map((frame) =>
frame.evaluate((ms) => {
window._timesnap_processUntilTime(ms);
return window._timesnap_runFramePreparers(
ms,
window._timesnap_runAnimationFrames
);
}, time)
)
);
};

如果 Puppeteer 打开的网页报错了(Uncaught xxxError),这里就会因为未捕获错误而导致程序中断,进而导致截屏中断。

如何捕获浏览器的报错信息

timesnap 有一个 logToStdErr 参数,但是不是用于这个的

正确的方式是通过 puppeteer 给浏览器注入自定义的 js,可以参考这个文章:

https://www.cnblogs.com/wuweiblogs/p/12918968.html

1
2
3
4
5
6
preparePage: (puppeteerPage) => new Promise((resolve, reject) => {
puppeteerPage.evaluate(() => {
document.body.style.backgroundColor = 'red';
})
resolve();
}),

不过由于我们目前有前端的 femonitor 了,其实可以直接在网页里面通过 femonitor 捕获到错误,上报给我们。