前言
最近做了个截图功能,使用的是html2canvas,当截取高德地图的时候出现白屏,地图信息无法正确渲染,但是地图容器也确实获取到了。由此猜测可能是高德地图Api没有正确设置,下面简述一下我的排查过程。
截图出现白屏
这里使用html2canvas将指定id为mapContainer的HTML元素及其子元素转换成Canvas对象,并提供一个下载链接。
import html2canvas from 'html2canvas'
// 实现截屏功能
const captureScreenshot = () => {
html2canvas(document.getElementById('mapContainer')!)
.then(canvas => {
const screenshotUrl = canvas.toDataURL('image/png')
setScreenshotUrl(screenshotUrl)
console.log('🚀 ~ AMapExample ~ screenshotUrl:', screenshotUrl)
const link = document.createElement('a')
link.download = 'screenshot.png'
link.href = screenshotUrl
link.click()
})
.catch(err => {
console.error('Screenshot failed', err)
})
}
下载地址可以正确获取,但是下载图片后发现截图是个白屏,可以看到地图logo都已经正确显示出来了,但并没有获取到地图数据。
好在控制台蹦出了一个警告
意思大概是当设置preserveDrawingBuffer=false时,不能克隆WebGL上下文,导致无法拿到绘图得到的地图数据。
排查过程
那么问题只能出在两个地方,要么是html2canvas要么是AMap,首先看html2canvas,找到对应的源码报警告的位置,在第5254行代码。
可以看到在这个
createCanvasClone方法是将原canvas转换为DataURL并创建一个img元素作为克隆,通过检查WebGL的上下文(gl)的属性对象(attribs)为null、undefined或其preserveDrawingBuffer属性为false,则抛出这个警告。很明显我们传入的canvas不可能为null或者undefined,那么问题就出在preserveDrawingBuffer为false这里。修改第5350行的源码,将gl的preserveDrawingBuffer属性设置为true,创建一个带有保留绘图缓冲功能的 WebGL 上下文。在此之后继续进行其他的 WebGL 操作,而绘图缓冲的内容将会保留,直到手动清除它或下一次绘制发生。
var gl = (_a = canvas.getContext('webgl2')) !== null && _a !== void 0 ? _a : canvas.getContext('webgl', { preserveDrawingBuffer: true });
本来以为这里已经好了,但是重新打开项目发现下载的仍然是空白页,想了很久觉得可能是Vite缓存的原因,重新启动vite后截图仍然展示白屏。那么就可能一个原因,html2canvas在获取Amap渲染的上下文时就已经清除了绘图缓冲,所以即使html2canvas设置preserveDrawingBuffer=true也获取不到渲染的数据。
解决方案
那么解决方案就很简单了,我只要让Amap配置中的preserveDrawingBuffer设置为true即可,查询Amap的配置有一个WebGLParams选项,配置preserveDrawingBuffer: true即可。
const amap = new AMap.Map('mapContainer', {
WebGLParams: {
preserveDrawingBuffer: true
},
})
解决了这里之后html2canvas的源码也不需要修改了,重新启动截图就有数据显示了。
反思
尝试解决问题的方式比较粗暴,没有想清楚问题出现的前因后果,应该仔细排查问题出现的源头,而不是头痛医头,脚痛医脚。此外打开preserveDrawingBuffer可能会引发一些性能问题,后续有机会学习WebGL再详细了解。