先来看看实现效果:
如上所示,此次实现的功能是输入网址后生成全屏截图,可用于生成图片版文章,也可以用做图片二维码海报的生成器,可以支持各种尺寸定义,下面讲解实现的一些技术细节。
实现截图
主要使用的是 puppeteer 这个库,最简单地实现一个网页截图,只需要几行代码即可:
const puppeteer = require('puppeteer') // 引入
// 启动浏览器
const browser = await puppeteer.launch({
headless: false, // 本地测试先关掉无头模式
})
// 创建一个页面
const page = await browser.newPage()
// 设置浏览器视窗
page.setViewport({
width: 300,
height: 600,
})
// 输入网页地址
await page.goto(url, { waitUntil: 'domcontentloaded' })
// 开始截图,全屏截图的关键参数就是这个fullPage,页面会一直滚动到底
await page.screenshot({ path: '{保存图片的路径}', fullPage: true })
// 关闭浏览器
await browser.close()
让网页接管截图动作
当我们使用浏览器截图方案作为内部业务方法使用的时候,为了保证生成图片的完整性,就需要等待异步操作执行完毕再开始截图,比如图片资源是否加载完成以及接口数据请求结果等判断,对于浏览器来说无能为力,而我们并不希望使用等待的方式来解决问题,而是让程序本身决定何时开始截图,这时我们可以通过 puppeteer 向页面注入一个全局方法,然后在目标页面中处理好资源的准备判断后,调用该方法,则可以实现页面对截图操作的控制。
// puppeteer 注入全局方法
await page.exposeFunction('loadFinishToInject', async () => {
// console.log('-> 开始截图')
await page.screenshot({ path, fullPage: true })
// 关闭浏览器
await browser.close()
})
// 目标页面/业务页面/截图页面:
..... Some Preload Code Function ....
console.log('--> 可以开始截图')
try {
window.loadFinishToInject() // 触发截图方法
} catch (err) {}
立即开始截图
通常情况下,我们可能只需要等待网页加载完成后就立即生成截图:
// 方法一:利用自带事件
await page.goto(url, { waitUntil: 'domcontentloaded' })
// console.log('-> 开始截图')
await page.screenshot({ path })
await browser.close()
waitUntil 说明:
- load: window.onload 事件被触发时继续。
- domcontentloaded: Domcontentloaded 事件触发时继续。
- networkidle0: 在 500ms 内没有网络连接时(全部的request结束)则继续。
- networkidle2: 500ms 内有不超过 2 个网络连接时就算成功(还有两个以下的request)则继续。
// 方法二:利用回调函数
page.on('load', async () => {
// 开始截图
await page.screenshot({ path, fullPage: true })
await browser.close()
})
以上代码片段均是基于本地开发时的情况来写,后面将会介绍到服务器上部署的一些问题。
本服务默认使用Express作为框架,不展开介绍。