1、需求:长按页面中的一部分(里面有动态获取的用户昵称、头像及动态生成的二维码),弹出下载框,点击后将这部分保存为图片下载到手机里(如图)
2、分析:由于有动态获取数据,需等DOM元素生成之后,再将这一部分的DOM转化为canvas,再将canvas转为image,然后再实现长按image下载到本地 - - 一路走来,踩了不少坑,希望有此相似需求的能有所收获吧。
3、过程
第一步:数据渲染后,将 html 转化为 canvas
html2canvas.js可将一个元素渲染为canvas,只需要简单的调用html2canvas(element[, options])方法即可,html2canvas方法会返回一个包含有canvas元素的promise。
// 参数:element为要保存元素的DOM对象,option为可配置项
html2canvas(element, option).then(function(canvas) {
});
重点1:清晰度问题(最终图片的清晰度取决于html转换成的canvas的清晰度)
<1> 将原来的DOM元素的宽高设置为最终图片的2倍,然后canvas的宽高也要设置最终图片的2倍。(最好为设备的DPR倍)
<2> 设置原来的DOM元素的宽高时必须以px
为单位,避免样式二次计算导致的模糊,切记~
例如:我的最终图片样式
// 生成的最终图片
#inviteImg {
width: 5.6rem; // rem适配:5.6rem * 50px/rem = 280px
height: 6.3rem; // rem适配:6.3rem * 50px/rem = 315px
}
则: 我的原来的DOM元素的样式
// 原来DOM盒子
#inviteBox {
width: 560px; // 最终图片宽度280px的2倍,以px为单位
height: 630px; // 最终图片高度315px的2倍,以px为单位
position: absolute;
z-index: -1; // 离开屏幕
background: transparent url("../assets/img/no.png");
background-size: 560px 630px;
}
则:canvas的宽高
let self = this
let inviteBox = document.getElementById("inviteBox")
let canvas = document.createElement("canvas")
canvas.width = 560 // 最终图片宽度280px的2倍,以px为单位
canvas.height = 630 // 最终图片高度315px的2倍,以px为单位
let opts = {
canvas: canvas, // 将自定义canvas作为配置项
useCORS: true, // 允许图片跨域
height: self.$refs.main.offsetHeight // 修复截图不完整问题
}
html2canvas(inviteBox, opts).then((canvas) => {
/* 此处的base64ImgSrc就是得到的img的base64字符串,直接在页面上显示即可 */
let base64ImgSrc = canvas.toDataURL("image/png")
})
重点2:截图不全问题(当截图区域高度超出可视区域会截图不完整)
由于官网上提供的dom抓取不支持高度,都是从绝对定位的起点开始截取,会造成只可以截到浏览器可见的,如果截图区域高度超出可视区域会截图不完整
可通过修改源码,可以支持完整截图(可以动态设置截图高度)
未修改源码如下:(可通过查找进行修改~)
return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {
if (typeof(options.onrendered) === "function") {
log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas")
options.onrendered(canvas)
return canvas
}
})
修改后源码如下:
/* 添加自定设置高度宽度 */
var width = options.width != null ? options.width : node.ownerDocument.defaultView.innerWidth
var height = options.height != null ? options.height : node.ownerDocument.defaultView.innerHeight
return renderDocument(node.ownerDocument, options, width, height, index).then(function (canvas) {
if (typeof(options.onrendered) === "function") {
log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas")
options.onrendered(canvas);
}
})
修改源代码后重新引入修改后的html2canvas.js(需放到本地了),此时可将你要截取的高度通过配置项传入即可。
/* 将要截取的高度设置为最大盒子的高度并通过配置项传入 */
let self = this
let opts = {
canvas: canvas,
useCORS: true,
height: self.$refs.main.offsetHeight // 修复截图不完整问题
}
第二步:将canvas转化为png格式的image
上一步生成的canvas即为包含目标元素的元素对象。实现保存图片的目标只需要将canvas转image即可。
html2canvas(inviteBox, opts).then((canvas) => {
let base64ImgSrc = canvas.toDataURL("image/png")
/* 如果只是显示,可用以下代码 */
let img = document.createElement("img")
img.src = base64ImgSrc
document.body.appendChild(img)
})
重点:base64格式图片在前端可以显示,但是无法下载到本地,所以要将base64格式的图片转化为网络路径的png格式图片
我项目的解决方法为:将base64编码上传到七牛云然后获取到网络路径的png格式的图片,具体实现方法可参考:
如何上传base64编码图片到七牛云
上传base64图片到七牛云存储
第三步:长按image保存到本地
长按事件见:H5–(封装)移动端原生长按事件
下载保存图片:如果在pc端,且不考虑兼容性的话,可以使用htm15的a标签增加了download属性,可利用此属性实现图片下载,具体我就不细说,因为在手机端的webview中不好使。
我项目的解决方法为:与原生客户端交互,调取客户端的下载方法,传入图片的网络途径,客户端协助进行下载并存入相册
。(ps:下载图片在webview中用h5真的实现不了,所以求助一下客户端小伙伴了~)
4、参考文档:
1、基于html2canvas实现网页保存为图片及图片清晰度优化 - - 小小云朵
2、html2canvas.js网页截图功能(解决截图不全问题) - - JugglerTao