欢迎关注@丁香园F2E查看更多技术好文

1. 需求

最近产品大大又又又搞事情,说什么想要在微信H5项目中做一个医生邀请提问的功能,可以将医生的二维码名片分享出去,之前图片由后端生成并且会缓存三天导致信息更新不及时;

我一听,这好说,不就是个保存图片的功能么,简单的看看需求:

  • 完善卡片信息,分享出去时候信息更加立体
  • 编辑个人资料入口
  • 保存图片入口
  • 可解决医生名片缓存时间问题
  • 长下面这样 ⬇
image

分析下来就两点

  • html展示实时用户信息
  • 点击保存将当前页面保存成图片至本地,并且不包含功能按钮

2. 方案

因为之前已经听说过有个库能将HTML转为canvas,然后又听说canvas能转为图片,然后又听说图片能下载....(开发基本靠听说,这是废话)

那我的基本方案就是:
html -> canvas -> image -> a[download]

  1. html2canvas.js:可将htmldom转为canvas元素
  2. canvasAPI:toDataUrl()可将canvas转为base64格式
  3. 创建a[download]标签触发click事件实现下载

3. 开发

既然方案定下来那么说干就干,下面请开始我的踩坑表演,?

3.1 html2canvas.js

官方是这样介绍的:

js将遍历加载页面的DOM节点,收集所有元素的信息,然后用这些信息来呈现页面。换句话说,实际上这个库并不是真的对页面进行截图,而是基于从DOM读取的元素及属性来一点点的绘制canvas。 因此,它只能正确地呈现它理解的元素和属性,这意味着有许多CSS属性不起作用。

// v0.4.1
html2canvas(element, {onrendered: function(canvas) {// 现在你已经拿到了canvas DOM元素    }
});// v0.5.0
html2canvas(element, options).then(canvas => {// 现在你已经拿到了canvas DOM元素
});复制代码

3.1.1 原理

该库的原理是将选中的dom及所有子节点全部跑一边,将每个节点用对应的方法挨个绘制到canvas中。

所以基本可以猜到整个工作流程应该是:

  1. 递归处理每个节点,记录这个节点应该怎么画。(比如div就画边框和背景,文字就画文字等等)
  2. 考虑节点的层级问题。比如z-index,float, position等样式的影响。
  3. 从低层级开始画到canvas上,逐渐向上画。层级高的覆盖层级低的。

3.1.2 坑

目前官方提供的版本有很多,正式版本是v0.4.1 - 7.9.2013,最新版本是v0.5.0-beta4,那对于我们开发来说如果不是玩新特性什么的一般还是会选择正式版,结果第一个坑就掉进去爬了半天。。

3.1.2.1 图片模糊

因为开发的时候是用chrome模拟器所以生成canvas后没有发现有模糊的地方,但是用pc代理手机请求开发资源时很明显的发现画面模糊的感觉非常明显

手机端截图,有很明显的锯齿感

image

那么就想到可能是移动端像素密度计算的问题

设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:

设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向

知道了这个也没用,因为文档中根本没有给出能够配置像素比的地方。。

那么通过搜索后发现,官方文档其实还是0.4.1的,从0.5.0版本开始其实已经支持自定义canvas作为配置项传入了,它会根据我们传入的canvas为基础开始绘制。所以我们在调用html2canvas的时候,可以先创建好一个尺寸合适的canvas,再传进去。

那么话不多说,首先将库升级到0.5.0

/*** 根据window.devicePixelRatio获取像素比*/
function DPR() {if (window.devicePixelRatio && window.devicePixelRatio > 1) {return window.devicePixelRatio;}return 1;
}
/***  将传入值转为整数*/
function parseValue(value) {return parseInt(value, 10);
};
/*** 绘制canvas*/
async function drawCanvas(selector) {// 获取想要转换的 DOM 节点const dom = document.querySelector(selector);const box = window.getComputedStyle(dom);// DOM 节点计算后宽高const width = parseValue(box.width);const height = parseValue(box.height);// 获取像素比const scaleBy = DPR();// 创建自定义 canvas 元素const canvas = document.createElement('canvas');// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比canvas.width = width * scaleBy;canvas.height = height * scaleBy;// 设定 canvas css宽高为 DOM 节点宽高canvas.style.width = `${width}px`;canvas.style.height = `${height}px`;// 获取画笔const context = canvas.getContext('2d');// 将所有绘制内容放大像素比倍context.scale(scaleBy, scaleBy);// 将自定义 canvas 作为配置项传入,开始绘制return await html2canvas(dom, {canvas});
}复制代码

手机端截图,和html展示效果一致,基本看不出来差别

image
3.1.2.2 图片画出来怎么不见了

PC端截图

image

可能有多种原因,最主要的原因是图片跨域并且服务器未设置允许跨域??这里有解释

看完发现我们好像需要做两件事:

  1. 给img元素设置crossOrigin属性,值为anonymous
  2. 图片服务端设置允许跨域

第一件事其实忽略,因为html2canvas支持配置useCORS: true

但是第二件事有点难办,因为我们的图片一般都是上传到CDN上,而CDN为了更快的响应,会缓存图片的返回值,而缓存的值是不带跨域的头的。因为没有跨域的头,所以js请求会被拦截。但这是cdn不在我们手里的情况,如果在了那不就是后端改个配置的事情么,哈哈哈哈。

PC端: 完美。

微信环境下如果使用0.5.0也没有问题,但是使用0.4.1时绘制canvas的还是会导致图片丢失。。只能猜测是在html2canvas在预载图片和绘制图片时少了不可描述的东西。

因为一开始使用0.4.1所以这个坑我没有绕过去,强行解决:

// 请求图片的事自己来做;将图片转为base64之后放回img的src中再进行绘制
/*** 图片转base64格式*/
img2base64(url, crossOrigin) {return new Promise(resolve => {const img = new Image();img.onload = () => {const c = document.createElement('canvas');c.width = img.naturalWidth;c.height = img.naturalHeight;const cxt = c.getContext('2d');cxt.drawImage(img, 0, 0);// 得到图片的base64编码数据resolve(c.toDataURL('image/png'));};crossOrigin && img.setAttribute('crossOrigin', crossOrigin);img.src = url;});
}复制代码

!注意:前提是服务端必须支持跨域,如果是无法改变服务端配置的图片最好提前砍掉,比如绘制微信头像

3.1.2.3 倒角

border-radius 只能是≤短边长度的一半,并且是具体数值,否则可能会出现奇妙的效果。

另外使用伪元素实现0.5px边框也可能会出现奇妙效果,建议直接使用border属性

0.4.1版本中需要做圆形图片只能置为背景图,img不支持绘制border-radius0.5中无此限制

3.1.2.4 虚线

使用border-style: dashed/dotted无效,还是大实线,切图在PC端有效,微信中需转为base64并且依赖环境,可能无效。

3.2 toDataUrl()

只要canvas中没有跨域图片可以随便转

But 在微信中就算没有也可能失败,在尝试使用切图渲染虚线时微信中还是会报SecurityError, The operation is insecure.错误,导致转base64失败

3.3 保存

理想

/*** 在本地进行文件保存* @param  {String} data     要保存到本地的图片数据* @param  {String} filename 文件名*/
saveFile(data, filename) {const save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');save_link.href = data;save_link.download = filename;const event = document.createEvent('MouseEvents');event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);save_link.dispatchEvent(event);
}复制代码

现实

PC端: 完美。微信:不好意思,你说什么?我听不见??!!

微信中根本没有任何反应。查看微信sdk后发现:

  • downloadImage仅支持uploadImage接口上传的图片
  • uploadImage接口仅支持chooseImage接口相册选择的图片
  • chooseImage接口....
  • 妈蛋都在相册了还玩个毛线。
  • ....

4. 交付

最终实现的方案是:

  • 用户进入该页面
  • 获取当前用户所有信息,头像,二维码等
  • 将所有图片转为base64
  • 渲染html
  • 绘制canvas
  • 转为base64
  • 替换htmlimgsrc为base64
  • 完成页面到图片的转换,微信中用户可长按页面调起actionSheet识别或保存图片
  • 在回头看我们的需求
    • html展示实时用户信息
    • 点击保存将当前页面保存成图片至本地

其实只实现了第一点,第二点仅实现了一半,保存功能还是需要用户调起微信内置API来完成,而不是我们帮用户完成;微信他不给这个接口我也很绝望啊/(ㄒoㄒ)/~~

希望以上内容能够对大家以后的开发有所帮助,谢谢。

完。

转载于:https://juejin.im/post/5a184af86fb9a04515439296

微信H5页保存当前页面为图片踩坑相关推荐

  1. iphone微信 h5页音乐自动播放

    iphone微信 h5页音乐自动播放: // iphone自动播放document.addEventListener("WeixinJSBridgeReady", function ...

  2. 将图片生成PDF的项目,要求每张图片生成一页,并且页面与图片尺寸大小相同

    将图片生成PDF的项目,要求每张图片生成一页,并且页面与图片尺寸大小相同 最近公司做项目碰到pdf生成,需要把填充的图片宽高平铺到每张页面上面,在百度搜了好久都没有朋友遇到,所以自己试了好多终于给搞定 ...

  3. 利用convert【ImageMagick】把pdf批量转换为图片踩坑(gs报错和清晰度问题)

    利用convert[ImageMagick]把pdf批量转换为图片踩坑 前言 坑1 gs报错 解决方案: 坑2 导出的文件太模糊 解决方案 参数介绍: 总结 最后附上完整的命令 前言 最近做一个pdf ...

  4. 在typora中存储图片踩坑总结(最完整版,包括图片的整体转移)

    在typora中存储图片踩坑总结(最完整版) 1.首先是之前一直以为建立一个文件夹存储图片就可以了,不料这个软件存储图片在绝对位置,所以这样你图片的文件夹移动之后就显示不了,这样就很尴尬!!比如下面这 ...

  5. h5游戏使用微信h5支付,刷新页面问题解决

    遇到的问题: h5游戏,或任何h5程序,使用微信h5支付后,自动刷了页面,导致用户体验极差 问题根本原因: h5端使用微信支付时,使用了以下代码 window.localtion.href = &qu ...

  6. 微信H5支付,HTML页面代码

    微信H5支付,前端HTML代码 说明 test.com是服务器地址,createH5Order接口要调用微信的统一下单接口,并返回微信返回的mwebUrl. <html> <body ...

  7. H5跳转微信小程序-成功案例(VUE)(踩坑无数)

    这里写自定义目录标题 准备工作 根据官方提供的资料需准备以下几点: 1.已认证的服务号 2.绑定JS接口安全域名 (在微信公众平台设置) 3.IP白名单 (在微信公众平台设置) 4.将小程序和H5公众 ...

  8. 微信小程序生成分享图片踩坑大计划

    微信小程序有个非常好的缺点,就是分享不能分享到朋友圈,怎么办呢,那只好生成图片,图片里面加个小程序码. 效果图 但不过其中有坑大家注意啦 HTML代码,我这个是不显示图片的生成图片,所以canvas设 ...

  9. 微信H5纯签约前端开发范围及相关坑

    开发内容: 1.提供签约成功回调地址notify_url,以供签约后获取并处理签约结果, 2.微信签约页链接由后台获取拼接后返给前端,location.href跳转即可. 参数来源:签名.用户ip等参 ...

  10. Python爬虫-爬取福利图片踩坑

    欢迎大家访问我的个人博客:https://jmbaozi.top/ 这个个人博客是我无意中发现的,经过Ping测试服务器应该是在海外,所以传输速度并不理想,为了减轻博主服务器的压力,在本文中进行脱敏处 ...

最新文章

  1. Netty结合Protostuff传输对象案例,单机压测秒级接收35万个对象
  2. 【Tools】python环境操作笔记
  3. Mysql下载以及安装(新手入门,超详细)
  4. 统计插件_CG Teamwork统计提交量插件制作思路
  5. HTTP系列之:HTTP中的cookies
  6. mysql001创建数据库
  7. 美团Android自动化之旅—生成渠道包
  8. SpringBoot Scheduled Cron表达式范例记录
  9. python os模块下载_python os模块
  10. linux脚本加标题,bash-从shell脚本设置屏幕标题
  11. 怎么使用PVS stream Linux
  12. 中文GRasshopper插件lunchbox(午餐盒),首发哦!
  13. 俄罗斯的程序员工资高吗
  14. html 如何关闭自动填充,如何禁止浏览器自动填充
  15. 区块链通证经济的核心不在技术,而在于商业逻辑的重构
  16. 落花已去,相思成冢。十月的杜鹃雨,下得纷纷扬扬。我走在花瓣雨下,回忆我们曾经的甜蜜温馨,一回首,一抬头,仿佛你就在灯火阑珊处。那些掉落在地上的杜鹃,成了相思的墓,也许是为了祭奠我们曾经的美好。 杜鹃
  17. 分布式系统(微服务架构)的一致性和幂等性和相关概念解析
  18. input number 数字输入限制,最大值最小值输入范围限制
  19. 论文解读:ChangeFormer | A TRANSFORMER-BASED SIAMESE NETWORK FOR CHANGE DETECTION
  20. 计算机图形学原理及实践学习笔记第一章

热门文章

  1. 【Python爬虫】淘宝商品比价定向爬虫
  2. ESP8266/ESP-01固件下载方法
  3. jquery-重要的方法和注意事项
  4. PyTorch: TORCHVISION.TRANSFORMS
  5. 【Android开发经验】Android相关问题的好文章整理——温故而知新,可以为师矣
  6. js html显示emoji表情,js emoji表情长度判断
  7. Dubbo 线上 Thread pool is EXHAUSTED 问题跟踪
  8. 矢量网络分析仪是什么?矢量网络分析仪的组成
  9. 蜂鸣器干扰通讯_提高蜂鸣器响度和降低蜂鸣器及驱动电路干扰电源的电路的制作方法...
  10. QT打包应用程序文件步骤