网页截图功能调研

背景

在某一开发项目中有这样一个需求,用户框选出一段内容给后端做OCR识别。
咋一看挺简单的,就是个鼠标框选功能,但是如何拿到框选区域的内容呢???换个角度来看这不就是QQ截图么,拿到截取的图片传给后端问题就解决了。
别急别急啊,很可惜的是JS并没有提供相关的方法,浏览器也没有提供对应的API。😩 😫

网页截图的两种方式

前端要实现页面截图的功能,现在比较常见的方式是使用开源的截图npm库,一般使用比较多的npm库有 html2canvasdom-to-image
以上两种常见的npm库,对应着两种常见的实现原理。实现前端截图,一般是使用图形API重新绘制页面生成图片,基本就是SVG(dom-to-image)和Canvas(html2canvas)两种实现方案,两种方案目标相同,即把DOM转为图片。

dom-to-image

dom-to-image库主要使用的是SVG实现方式,简单来说就是先把DOM转换为SVG然后再把SVG转换为图片。
具体用法这里不做细究,只做基本介绍

1
2
3
4
5
6
const node = document.getElementById('node');
domtoimage.toPng(node,options).then((dataUrl) => {
const img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
html2canvas

html2canvas库主要使用的是Canvas实现方式,主要过程是手动将dom重新绘制成canvas,因此,它只能正确渲染可以理解的属性,有许多CSS属性无法正确渲染。

1
2
3
html2canvas(dom, option).then(canvas=>{
canvas.toDataURL()
})

通过对比两者的基本用法发现,它们都需要传入一个具体的dom对象才能使用,但是用户截图所选内容不一定是一个完整的dom,而且就算是一个完整dom我们也没法获取到对应的dom。否则就会不需要大费周章的使用三方件了。完了此路不通啊
不要急少年,直接使用不行那就迂回吧。首先通过上面两种方法将整个网页转换成图片,然后我们在图片里截取所需内容就OK啦。
这就用到了canvas的
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage

null
基本用法如下:

1
2
// 传入截取原图,起始点距左上角的位置和截取的宽高
ctx.drawImage(image, 33, 71, 104, 124, 21, 20, 87, 104);

很可惜SVG并没有相同的能力,所以dom-to-image不适用我们的任意截图功能。

框选功能

这个很简单,就是监听鼠标的mousedown、mousemove、mouseup事件获取鼠标点击位置和移动距离。在mouseup里传入所得到的的位置信息给canvas。

1
2
// 获取包含图片的base64,也可以使用使用别的方法直接下载图片,核心就是拿到图片信息
canvas.toDataURL();

到此一个简单的网页任意截图功能就完成了。下面回归到我们的需求本身

业务分析

首页项目中需要截图的内容本身就是一个图片,那么其实就不需要使用三方件搞得那么复杂。就是一个框选功能加新建canvas再调用drawImage方法已经满足需求了。

总结

虽然最终需求实现不是太复杂,但是在调研过程中我们也理清了如何实现一个完整的截图功能,重新熟悉了canvas相关的知识也算是颇有收获。后期遇到一个真正的截图功能也我们可以从容胜任。好了就这样吧。