持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
如何在页面中判断图片已加载完成
主动调用注入函数进行截图的场景,通常都是我们自己的业务页面,这时我们可以在页面中对资源加载情况进行判断,来决定截图的时机。
起初我的想法是把图片url链接传进一个处理函数,函数中用Image
对象加载src
,当实例对象触发onload
时就接着下一个,直到全部处理完毕那么也就算资源加载完成,但是很快我发现这样处理并不对,因为这个函数实际是异步的,很可能先加载完了资源但是该资源在页面中却并未加载完成,所以正确的方式应该是获取到页面当中真实的资源DOM节点,传入这个函数中处理,而且onload
回调也不一定正确,经测试最稳定的方式是轮询complete
属性来确定是否真的加载完成。
// Preload.ts 参考代码
export default class PreLoad {
private i: number
private arr: any[]
constructor(arr: string[]) {
this.i = 0
this.arr = arr
}
public doms() {
return new Promise((resolve: Function) => {
const work = () => {
if (this.i < this.arr.length) {
this.arr[this.i].complete && this.i++ // 核心是轮询img节点的complete属性
setTimeout(() => {
work()
}, 100)
} else {
resolve()
}
}
work()
})
}
}
假设业务页面当中,内容是通过接口请求到前端渲染,为每个图片的div容器我都加上了img__box
这个class样式,即<div class="img__box"> <img /> </div>
这种形式,那么我可以这么处理:
const imgsData = []
const cNodes = document.querySelectorAll('.img__box')
for (const el of cNodes) {
imgsData.push(el.firstChild)
}
const preload = new Preload(imgsData)
await preload.doms() // 实例化上面的Preload函数,开始轮询资源
console.log('--> 加载完成,可以开始截图')
try {
window.loadFinishToInject('done') // 触发`Puppeteer`的注入方法
} catch (err) {}
懒加载页面处理方法
有时我们会遇到截取页面资源是懒加载的情况,像生成第三方网页文章时会非常不稳定,而且也不能通过单纯的sleep
等待函数来解决问题。
所以我们需要加一个自动滚动的方法,来模拟真实的页面浏览触发页面的资源懒加载。
实现的核心是利用 Puppeteer
的 evaluate
函数,改方法可以在目标页面上下文中执行JS代码,简单的触底判断:比较两次滚动后的scrollTop
是否一致,如果一致就是页面不再往下滚了,即判断为触底。
// 创建自动滚动函数
async function autoScroll() {
await page.evaluate(async () => {
await new Promise((resolve, reject) => {
try {
const maxScroll = Number.MAX_SAFE_INTEGER
let lastScroll = 0
const interval = setInterval(() => {
window.scrollBy(0, 100)
const scrollTop = document.documentElement.scrollTop || window.scrollY
if (scrollTop === maxScroll || scrollTop === lastScroll) { // 判断触底,或超出js最大安全长度
clearInterval(interval)
resolve()
} else {
lastScroll = scrollTop
}
}, 100) // 100毫秒执行间隔
} catch (err) {
console.log(err)
reject(err)
}
})
})
}
在截图前加入该函数:
page.on('load', async () => {
await autoScroll() // 自动截图时先模拟滚动
await sleep(wait) // 前面实现的等待方法
// 开始截图
await page.screenshot({ path, fullPage: true })
// 关闭浏览器
await browser.close()
})
这样当页面加载完成后,就会触发自动滚动,每100毫秒向下滚100像素,直到触底为止,跳出Promise,配合前面我们实现的wait
参数,等待 x 毫秒后开始截图(这里滚动只是触发了资源加载,如果不等待一下资源有可能没加载完)
最终结果如下,大部分页面的情况应该都差不多:
正常截图 | 加入自动滚动 |
---|---|
下一篇文章讲讲服务器部署的相关细节。