前端实现截图功能浅析

5,168 阅读2分钟

通过前端的手段实现截图功能,主要分为两种情况:

  1. 对浏览器网页页面的截图。
  2. 仅对canvas画布的截图,比如可视化图表、webGL等。

对于第一种情况,将浏览器网页页面截图,在前端无权限控制浏览器相关接口的情况下,要实现网页的截图,主要思路还是靠页面dom转化成canvas或者svg图片来解决。有两个比较著名的库html2canvasdom-to-image便提供了这种功能,当然,这里面有一些限制和坑。

实际应用上,在pc端这种需求不算多,而且浏览器本身就提供了网页的截图功能,并不需要前端程序来操控。感兴趣的可以查看这篇文章了解详情,便不再赘述。

对于第二种情况,canvas画布截图,这种需求主要集中在保存图表、可视化界面等,不过实现起来十分简便,因为canvas它本身就有保存为图片的功能,它有两个API可以用来导出图片,分别是 toDataURLtoBlob

toDataURL方法是将整个画布转换成base64格式的url地址,toBlob方法是创造Blob对象,默认图片类型是image/png。

注:canvas的两个API toDataURL()toBlob()都受到同源策略的限制而无法跨域,也就是说如果canvas的画面来源是跨域请求,则调用该api会报跨域错误,常见解决方式是让后端设置允许跨域,前端将crossOrigin 属性值设置为anonymous 。

three.js做3D可视化界面为例,我们需要把3d模型截图保存下来。因为我们的模型是渲染在canvas画布上,所以可以直接通过toBlob方法来截图保存,我们将这些封装成一个截图方法:

image-20210529100143825

// 将截图方法封装成方法,传递两个参数:
// canvas:指定截图的canvas元素
// fileName:下载的文件名
const screenshotCanvas = (canvas, fileName) => {
  // 验证canvas元素
  if (
    !canvas ||
    canvas.nodeType !== Node.ELEMENT_NODE ||
    canvas.nodeName.toLowerCase() !== 'canvas'
  ) {
    throw new Error('请传入要截图的canvas元素')
  }

  try {
    canvas.toBlob((blob) => {
    	// 创建a元素来实现下载
    	const a = document.createElement('a')
    	a.style.display = 'none'
    	document.body.appendChild(a)
    	a.href = window.URL.createObjectURL(blob)
    	// 下载的文件名
    	a.download = fileName || `screenshot-${canvas.width}x${canvas.height}.png`
    	a.click()
    	// 触发下载后,移除元素
    	a.remove()
  	})
  } catch (e) {
    console.log(e)
  }
}

这样就可以将canvas画布内容截图保存下来了。

注:出于性能和兼容性的原因,默认情况下浏览器在绘制到WebGL画布的绘制缓冲区后会清除它,所以在截图之前一定要渲染一次,否则截图不出来。