工作总结(8)

这篇工作总结将整理一下如何实现html生成图片。

生成图片一般的思路都是将需要生成图片的dom绘制到canvas中,再调用canvas.toDataURL()将绘制好的canvas转成file对象,然后再上传服务器,拿到线上地址,再显示出来。

最麻烦的就是将dom绘制到canvas,摘自网上的说法,需要先遍历需要生成图片区域的所有元素,提取DOM数,然后获取渲染之后的每个DOM节点的内联、外链CSS属性,最后将样式转换成canvas的属性,利用offset等属性辅助摆放位置,将节点对应到canvas上。所以更多人实现的方案是使用第三方库html2canvas

html2canvas用法也很简单,就是一个html2canvas()方法,传入需要生成图片的dom跟配置,然后这个方法支持Promise,可以使用html2canvas(dom).then方法写入一个回调函数,拿到绘制完成的canvas,就可以完成接下来的处理了。

这里整理一下开发中遇到的需求跟处理。一般生成的图片会跟页面显示不同,因为可能会加一些内容。这样就需要在调用html2canvas()方法先调整需要生成图片的dom的样式,改成需要的样式效果。然后在回调函数中,等拿到canvas之后,再把dom的样式恢复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 使用vue开发
// 先引入html2canvas
htmlToImage () {
// 返回一个Promise,调用的时候可以知道什么时候结束,支持异步处理
return new Promise((resolve, reject) => {
// 保存需要修改的样式的当前值
// ...
// 设置需要打印的样式的宽高
// ...
// 调用$nextTick确保样式已经更新
this.$nextTick(() => {
// 调用html2canvas,传入需要生成图片的dom跟配置,配置可以参考官网
html2canvas(this.$refs.print, {
allowTaint: true, // 不允许跨域图片污染画布
useCORS: true, // 允许加载跨域图片
width: this.$refs.print.offsetWidth, // canvas宽度
height: this.$refs.print.offsetHeight, // canvas高度
}).then(canvas => {
// 拿到的就是绘制了dom的canvas
// 恢复修改前的样式
//...
})
}, 1000)
})
},

关于图片模糊的问题

上面的操作可以拿到canvas,然后就可以转file对象,上传,拿到服务端链接,然后打开一看,图片很模糊。

这个问题也很常见,一方面跟设备的dpi有关,另一方面跟生成的图片太小,拉伸之后也会出现这个问题。所以解决的方案是在调用html2canvas()方法先把需要生成图片的dom的整体样式放大数倍,然后再生成图片,同时在调用方法时设置dpiscale两个配置,这样能保证图片效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
htmlToImage () {
// 返回一个Promise,调用的时候可以知道什么时候结束,支持异步处理
return new Promise((resolve, reject) => {
// //保存需要修改的样式的当前值
let height = this.$refs.print.offsetHeight;
let width = this.$refs.print.offsetWidth;
// //设置需要打印的样式的宽高
this.$refs.print.style.height = height * 2 + 'px';
this.$refs.print.style.width = width * 2 + 'px';
// 调用$nextTick确保样式已经更新
this.$nextTick(() => {
// 调用html2canvas,传入需要生成图片的dom跟配置,配置可以参考官网
html2canvas(this.$refs.print, {
dpi: window.devicePixelRatio * 2, // dpi设置 每英寸的像素
scale: 2, // 放大倍数
allowTaint: true, // 不允许跨域图片污染画布
useCORS: true, // 允许加载跨域图片
width: this.$refs.print.offsetWidth, // canvas宽度
height: this.$refs.print.offsetHeight, // canvas高度
}).then(canvas => {
// 拿到的就是绘制了dom的canvas
// 恢复修改前的样式
//...
})
}, 1000)
})
},

关于跨域图片不显示的问题

所以又按照上面的思路改了一下,图片是终于清晰了,但是还有一个问题没解决,就是本来应该显示的图片没有出现。这个问题我搜索过之后才知道原来是图片跨域的问题。

一般图片跨域没什么问题,但是在canvas上,跨域图片会导致画布被污染,所以跨域图片不会被绘制到canvas上,最终生成的图片也没有正常显示该图片。

解决这个问题,我试过一些网上的简单的处理,都无效,所以只能把跨域图片转为本地图片,再设置html2canvas()useCORStrue

跨域图片转为本地图片,目前我的解决方案是让服务器返回二进制数据,然后转成Blob对象,再调用window下的createObjectURL()方法转成本地图片url。

关于base64转Blob对象转file对象

这个问题打算单独整理,就不再这里写了。