all articles

implementation of screenshot feedback

2017-02-01 @sunderls

js canvas

1. 背景

最近注意到知乎的屏幕截图反馈功能,感觉非常不错。也不知道什么时候开始增加的这个功能:

简单说明一下:

  1. 点击知乎页面右下角的反馈按钮,弹出反馈弹窗
  2. 窗口中会显示当前页面的截图,并且可以在上面做标记

这让我联想到很早之前Google Keep就提供了类似的功能:

说明:

  1. 点击google keep左侧边栏的反馈按钮,弹出反馈弹层
  2. 窗口中显示当前页面的截图,并且点击后可以进行更为详细的框选和涂抹:

2. debug

我的第一直觉是html2canvas,但是知乎的截图中的css复原的非常完美,相比之下google keep的截图就会有很多小地方发生了错位。

于是打开devTool一看源代码,发现知乎使用了rasterizeHTML.js:

3. rasterizeHTML

rasterizeHTML中对原理进行了说明,主要利用了<svg><foreignObject>,把网页作为外部资源嵌套在svg中,然后在把svg绘制在canvas中。具体原理和步骤说明在MDN的Drawing DOM objects into a canvas也有说明。

html2canvas的原理在其官网上也有写,就是去看一个个元素的具体样式,然后在canvas上进行重新绘制--这种实现办法不可避免的出现处理不完整的情况,比如css的特殊属性等。但是用svg的话,利用的直接是浏览器自身的渲染,所以可以达到非常完美的地步。

具体操作步骤MDN上有说明,最重要的是html中的外部资源需要同源,或者开启了cors。 图片的话data-url不受限制,所以知乎采用的是将图片src转换为data-url的形式,个人觉得用cors简单方便的多啊,因为data-url有长度显示,最好不能超过2KB。

当然,实际使用的时候,不会自己去处理,而是用rasterizeHTML,使用非常简单:

rasterizeHTML.drawURL( url [, canvas] [, options] )
rasterizeHTML.drawHTML( html [, canvas] [, options] )
rasterizeHTML.drawDocument( document [, canvas] [, options] )

和rasterizeHTML相似的library还有dom-to-image, domvas等,实际上很早以前就有了,只不过一直不知道有什么用,最近看来意见反馈的时候用这个是一种趋势。

4. 题外话

google keep中允许手动绘制区域,注意出现重叠的区域会合并在一起。对于这种mask选中效果,我知道的做法是:

  1. 加上一层半透明层
  2. 勾选区域设置为相同的背景
  3. 移动时更新勾选区域的背景位置background-position使得背景位置完全匹配。

具体效果可以看cropper.js

但是这种办法处理区域重叠的时候,就不行了,比如截图中的外部边界线。这怎么搞的? 只好debug一下,发现用的是canvas:

上面截图中右边部分是把canvas的内容单独输出出来的,可以看到canvas中的镂空部分正好是手动勾选的区域。 得到这个图可以在审查元素中选中<canvas> ,然后键入:

$0.toBlob((result)=>{
console.log(URL.createObjectURL(result));
})

5. 总结

canvas还是很强大的。