1、需求:长按页面中的一部分(里面有动态获取的用户昵称、头像及动态生成的二维码),弹出下载框,点击后将这部分保存为图片下载到手机里(如图)



2、分析:
由于有动态获取数据,需等DOM元素生成之后,再将这一部分的DOM转化为canvas,再将canvas转为image,然后再实现长按image下载到本地 - -
一路走来,踩了不少坑,希望有此相似需求的能有所收获吧。

3、过程

第一步:数据渲染后,将 html 转化为 canvas

html2canvas.js <https://github.com/niklasvh/html2canvas>
可将一个元素渲染为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编码图片到七牛云
<https://developer.qiniu.com/kodo/kb/1326/how-to-upload-photos-to-seven-niuyun-base64-code>
上传base64图片到七牛云存储 <https://blog.csdn.net/Jill6693484/article/details/57081815>

第三步:长按image保存到本地

长按事件见:H5–(封装)移动端原生长按事件
<https://blog.csdn.net/weixin_41076513/article/details/80110666>

下载保存图片:如果在pc端,且不考虑兼容性的话,可以使用htm15的a标签增加了download属性,可利用此属性实现图片下载,具体我就不细说,因为在手机端的webview中不好使。
我项目的解决方法为:与原生客户端交互,调取客户端的下载方法,传入图片的网络途径,客户端协助进行下载并存入相册
。(ps:下载图片在webview中用h5真的实现不了,所以求助一下客户端小伙伴了~)

4、参考文档:

1、基于html2canvas实现网页保存为图片及图片清晰度优化 - - 小小云朵
<https://segmentfault.com/a/1190000011478657>
2、html2canvas.js网页截图功能(解决截图不全问题) - - JugglerTao
<http://www.th7.cn/web/html-css/201707/232818.shtml>

友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信