需求
用户在页面填写信息和签字后要把当前页面的信息截图生成图片保存。
技术实现难点
- 页面长度特别长,多屏才能显示完全,生成的图片的长度特别长。
- 页面内容复杂,内容中包含了大量文本、大量图片。
- 页面源码使用了大量自定义组件。
常见技术方案
1、使用官方推荐的扩展组件:wxml-to-canvas
- 优点:经过大量项目实践,官方推荐,比较稳定。
- 缺点:需要手动拼接wxml,wxss,只适用于内容简单、内容量少的图片生成,比如海报。不支持自定义组件。
2、服务器端生成图片
- 优点:摆脱小程序束缚,节省小程序端开发工作量
- 缺点:损耗服务器性能,服务器压力大,图片生成能力有限。
在这个需求中显然上面两种方案都不太理想,甚至无法使用。小程序没有提供截图功能更不要说长截图功能。基于小程序现有的能力是实现不了了,只能自己寻找其他解决方案。
实现原理
在web端有很多把页面转为图片的工具库,能不能利用这些工具库或者移植到小程序上呢?
微信小程序底层虽然也是遵循web标准的,可是它自行实现了一套开发语言标准,这使得移植很难达成。让人感到欣慰的是微信小程序提供了webview组件以承载web页面。有了这个组件就可以借助web页面做很多事情了。该方案的实现正是基于这一个组件。
该方案的实现思路:小程序页面通过webview组件加载H5页面,然后在H5页面中引入html2canvas,利用html2canvas实现HTML到canvas的转换,再把canvas转换为图片进行上传保存。
这个方案可以完美解决前面文中提到的各个实现难点,而且该方案的代码也可以直接用于web端截图。具体实现代码如下。
-
// import html2canvas from '../Utils/html2canvas'
-
import '../Utils/html2canvas'
-
import Canvas2Image from '../Utils/Canvas2Image'
-
-
const makeImage = {
-
/**
-
*HTML生成图片并上传
-
*
-
* @param {Node} el DOM节点对象
-
* @returns 返回生成的图片的base64格式数据
-
*/
-
async makeHtml2Image (elementId = '', ignoreElementClass = '') {
-
try {
-
// 需要截图的包裹的(原生的)DOM 对象
-
const shareContent = elementId ? document.getElementById(elementId) : this.$el
-
-
const width = shareContent.offsetWidth // 获取dom 宽度
-
const height = shareContent.offsetHeight // 获取dom 高度
-
-
const scale = 1.5 // 定义任意放大倍数 支持小数
-
const canvas = document.createElement('canvas') // 创建一个canvas节点
-
canvas.width = width * scale // 定义canvas 宽度 * 缩放
-
canvas.height = height * scale // 定义canvas高度 *缩放
-
canvas.getContext('2d')
-
-
const rect = shareContent.getBoundingClientRect() // 获取元素相对于视口的
-
// 获取滚动轴滚动的长度
-
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
-
-
const opts = {
-
x: rect.left,
-
y: rect.top > 0 ? rect.top : 0,
-
// 为正数时说明页面向下滚动了,页面顶点处于视口中
-
// 为负数是说明页面向上滚动了,页面顶点处于视口上方,已无法在视口中展示
-
scrollY: rect.top > 0 ? -scrollTop : scrollTop,
-
scale: scale, // 添加的scale 参数
-
canvas: canvas, // 自定义 canvas
-
logging: true, // 日志开关,便于查看html2canvas的内部执行流程,默认为true
-
width: width, // dom 原始宽度
-
height: height,
-
useCORS: true, // 【重要】开启跨域配置
-
// 要忽略的节点
-
ignoreElements: el => ignoreElementClass && el.className.includes(ignoreElementClass)
-
}
-
-
// HTML转换为canvas
-
const htmlCanvas = await (window.html2canvas || html2canvas)(shareContent, opts)
-
const context = htmlCanvas.getContext('2d')
-
// 【重要】关闭抗锯齿
-
context.mozImageSmoothingEnabled = false
-
context.msImageSmoothingEnabled = false
-
context.imageSmoothingEnabled = false
-
-
// 转化img或者base64字符串 【重要】设置图片格式
-
const base64 = Canvas2Image.getImgBase64String(htmlCanvas, 'jpeg', htmlCanvas.width, htmlCanvas.height)
-
-
-
// 传递到WebView以供调试,在web端删除即可
-
window.wx.miniProgram.postMessage({
-
data: {
-
description: 'HTML截图',
-
res: base64
-
}
-
})
-
-
return base64
-
} catch (error) {
-
window.wx.miniProgram.postMessage({
-
data: {
-
description: 'HTML截图异常信息',
-
error: error
-
}
-
})
-
}
-
}
-
}
-
-
export default makeImage
注:
1、由于当前的html2canvas的1.0.0-rc5版中使用了documentClone.fonts.ready导致在iOS中不兼容,所以采用了修改过的代码——去掉了,修改后的代码地址:html2canvas_fixed。在web端中则不存在这个问题,可以直接通过npm安装依赖包然后导入。
2、代码中的Canvas2Image为把canvas转化为image的工具,地址:/wangzl1163/canvas2image。