需求背景:
用户通过在微信中分享海报图片邀请新用户注册,海报中有分享者参数的二维码,每个用户的二维码不同 所以海报中有些部分是动态的,不能直接使用图片,需要将html转换为图片。
技术点: html2canvas
html2canvas能够实现在网页或者部分内容直接进行“截屏”,其实现思路是 html2canvas脚本通过读取DOM和应用于元素的不同样式,将页面元素渲染为canvas。它不需要在服务器上进行任何的渲染,它整个图像都是在客户端的浏览器上创建的。因为是基于DOM的,所以它可能不是100%精确到真实的表示,它无法生成实际的屏幕截图,而是基于页面上可用信息构建图片。
兼容性
优缺点:
只需要简单调用html2canvas方法并且设定配置项即可,实现简单。在没有代理的情况下,页面中的图片不能跨域,需要结合属性进行跨域处理;对一些css属性支持不是很友好,不支持flash和iframe标签。
使用方法:
一、配置基本环境
- 引入html2canvas npm install --save html2canvas
- 把html2canvas引入到需要的组件中 import html2canvas from 'html2canvas'
二、将指定区域的内容转换为图片的方法:
-
获取需要转换为图片的内容节点DOM
-
html转换为canvas: 调用html2canvas函数可以将元素渲染为canvas,该函数返回一个Promise,然后Promise对象会将截取的图片参数传递参数canvas
-
调用canvas的toDataUrl方法将DOM输出为包含图片展示的data uri
代码实现:
- 首先需要让html2canvas获取到想要转换的节点,需要添加ref标记, 这个节点是要转换内容的父容器,所有的转化内容都要包含在里面。
<div class="post-con" ref="imgDom">
...想要生成图片的html代码
</div>
-
获取目标dom,然后调用 html2canvas 方法生成canvas对象。
createPicture() { this.$nextTick(() => { // 获取要转换的元素 const imgDom: any = this.$refs.imgDom // 拿到目标dom调用一下html2canvas方法就能生成canvas对象了 // 获取要转换的元素 html2canvas(imgDom, { useCORS: true // 开启跨域设置,需要后台设置cors }).then((canvas) => { // toDataURL函数生成img标签的可用数据 图片格式转成 base64 let dataURL = canvas.toDataURL("image/png") this.imgUrl = dataURL }) }) }
-
调用canvasAPI:toDataUrl() 可将 canvas 转为data: uri类型的图片地址,为 base64 格式,再把该图地址赋值给image标签的src属性即可。
<div class="img-con"> <img class="canvas-con" :src="imgUrl"/></div>
踩坑/优化
1、 生成图片清晰度优化
-h tml2canvas在高倍屏下可能会变得模糊,解决这个问题可以考虑 将canvas的属性width和height属性放大为2倍(或者设置为devicePixelRatio倍),最后将canvas样式width和height设置为原先1倍的大小。
createPicture() {
this.$nextTick(() => {
// 获取要转换的元素
const imgDom: any = this.$refs.imgDom
// 解决转换出来的图片的清晰度问题
// 手动创建一个 canvas 标签
const canvas = document.createElement("canvas")
// 获取父级的宽高
const width = parseInt(window.getComputedStyle(imgDom).width)
const height = parseInt(window.getComputedStyle(imgDom).height)
// 定义放大倍数,可支持小数
let scale:number = this.getPixelRatio()
// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
canvas.width = width * scale
canvas.height = height * scale
// 设定 canvas css宽高为 DOM 节点宽高
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
// 获取content,设置scale
const context = canvas.getContext("2d")
// context.scale(scale, scale)
// 拿到目标dom调用一下html2canvas方法就能生成canvas对象了
// 获取要转换的元素
html2canvas(imgDom, {
canvas: canvas,
scale: scale,
useCORS: true // 开启跨域设置,需要后台设置cors
}).then((canvas) => {
// toDataURL函数生成img标签的可用数据 图片格式转成 base64
let dataURL = canvas.toDataURL("image/png")
this.imgUrl = dataURL
})
})
}
// 动态获取设备的像素比,从而正确放大 canvas
private getPixelRatio() {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio
}
return 1
}
-
关闭抗锯齿,imageSmoothingEnabled是canvas用来设置图片是否平滑的属性,true表示图片平滑(默认值),false表示关闭canvas抗锯齿。默认情况下,canvas的抗锯齿是开启的,可以通过关闭抗锯齿来实现一定程度上的图片锐化,提高线条边缘的清晰度。
-
还有一些情况,导出图片模糊是因为原html页面重的图片是以css中background的方式设置的,因为background-size并不会反馈一个具体的宽高数值,导致生成的图片相对模糊,优先使用img标签展示图片。对于必须使用背景图background的元素,可以采用设置待优化元素宽高设置为 2 倍或devicePixelRatio倍,然后通过 css 缩放的方式控制其展示大小不变。
.img-con {
background: url(/path/to/image) no-repeat;
width: 100px;
height: 100px;
transform: scale(0.5);
transform-origin: 0 0;
}
- 为了给到html2canvas明确的整数计算值,避免因小数舍入而导致的拉伸模糊,建议将布局中使用%、vw、vh或者给rem等单位的元素样式,使用px来代替。
- html2canvas存在跨域资源无法加载问题,可以配置useCORS:true属性,服务端设置cors,即可实现canvas图片的转化和下载。