一、背景
在 内容合作协议H5 的业务中,提供一个H5页面,供运营&法务同学线上签约付费用户。其中有一个场景,需要生成“合同页”的长图片
解决思路: html -> canvas -> image -> a[download]
单击查看demo
demo源码地址:https://github.com/hfuuss/jiuyueTest/blob/master/html2canvas/index.html
二、解决方案
- 方案1:将DOM改写为canvas,然后利用canvas的toDataURL方法实现将DOM输出为包含图片展示的data URI
- 方案2:使用html2canvas.js实现(可选搭配Canvas2Image.js实现网页保存为图片)
- 方案3:使用rasterizeHTML.js实现
方案对比选择
- 方案1:需要手动计算每个DOM元素的Computed Style,然后需要计算好元素在canvas的大小位置等属性。
方案1难点:
相当于完全重写了整个页面的布局样式,增加了工作量。
由于canvas中没有的对象概念,对于元素丰富、布局复杂的页面,不易重构。
所有DOM元素改写进canvas会带来一些困难,例如:难以支持响应式,图片元素清晰度不佳和文字点击区域识别问题等。 - 方案2:该类功能中Github上stars最多(至今仍在维护),Stack Overflow亦有丰富的讨论。只需简单调用html2canvas方法并设定配置项即可。(配合 canvas2image可以很方便转换为图片)
- 方案3:该方案的限制较多,目前仅支持3类可转为canvas的目标格式: 页面url,html字符串和document对象。
小结: html2canvas是目前实现网页保存为图片功能的综合最佳选择。
相关文档:
html2canvas:https://github.com/niklasvh/html2canvas
canvas2image:https://github.com/hongru/canvas2image
三、生成图片的清晰度优化方案
因为canvas不是矢量图,而是像图片一样是位图模式的。如果不做Retina屏适配的话,例如二倍屏,浏览器就会以2个像素点的宽度来渲染一个像素,该canvas在Retina屏幕下相当于占据了2倍的空间,相当于图片被放大了一倍,因此图片会变模糊。
3.1 清晰度优化方案
要做Retina屏适配,关键是知道当前屏幕的设备像素比,然后将canvas放大到该设备像素比来绘制,然后将canvas压缩到一倍来展示。
- 涉及到的知识点:
要设置canvas的画布大小,使用的是canvas.width 和 canvas.height;
要设置画布的实际渲染大小,使用的style或CSS设置的 width 和height,只是简单的对画布进行缩放。
注意事项:canvas中的线条大小、文字大小等都需要乘以设备像素比来进行绘制,否则高倍屏下的线条会变细几倍
解决方案代码示例:1
2
3
4
5
6
7
8
9
10
/**
* 根据window.devicePixelRatio获取像素比
*/
function DPR() {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio;
}
return 1;
}
1 | /** |
3.5: 下载方案的坑
使用canvas2image的中的saveAsImage()的时候,在Chrome中的手机模式下调试的时候,网页会跳转到一个新的网页,但是不会下载图片。经过调研,采用新的下载方案:
旧方案:
1 | function saveFile (strData) { |
新方案:
1 | function saveFile (strData) { |
四、部分技术使用样例
html2canvas使用:
1 | html2canvas(document.body).then(function(canvas) { |
2. canvas2image 使用
1 | Canvas2Image.saveAsImage(canvasObj, width, height, type) |
3. html2canvas、canvas2image、jQuery结合使用
1 | $(document).ready( function(){ |
五、其它
由于html2canvas是将dom“画”的canvas上面,可能会受到许多布局属性的影响: z-index、float、position等
如果页面中存在跨域的图片,可能会导致无法“绘制”图片,解决方案需要俩点:
- 1、在html2canvas中配置
useCORS: true
。 - 2、利用CORS解决跨域问题。