小程序H5生成二维码海报图-插件html2canvas踩坑
需求场景
将小程序H5页面(含有小程序二维码)的一部分内容,或者全部内容生成一张图片,并以图片的形式进行如下核心操作: 保存到手机,分享给朋友(调起微信分享接口)等。 微信好友可以通过图片中的二维码扫码进入对应的小程序,实现用户裂变。
右下角为分享出去的二维码
选择该插件(html2canvas)的原因
Html2canvas加载后将会浏览页面上的所有元素,集合所有页面元素的信息,然后用户就可以通过Html2canvas把选取的页面截图下来(通过css选择器选取你想截取的页面)。 屏幕截图基于DOM,因此可能无法真正表示真实屏幕截图的100%,因为它无法生成实际的屏幕截图,而是根据页面上的可用信息构建屏幕截图。它不需要服务器提供任何渲染,因为整个图像是在客户端的浏览器上创建的。 这就会导致Html2canvas只会渲染它认识的正确的DOM元素属性,还有很多CSS属性是不会生效的,也就渲染不出来了。 综上所述。这个插件能够满足我们的需求。
插件的基本用法
-
npm安装 npm i html2canvas@1.0.0-rc.4 --save
-
项目引入 import html2canvas from 'html2canvas'
a. 项目中采用rc.4版本。 最新rc.7版本在ios13系统中存在不兼容。
-
基本使用
踩坑
-
图片跨域导致截图对应区域空白
出现原因:因为html页面内,必定含有用户头像,二维码等后台返回的图片,html的img标签src属性不受跨域影响,故html页面不受影响 。但canvas不支持跨域。当我们尝试生成canvas时,会报错。
cors
什么是 cors : developer.mozilla.org/zh-CN/docs/…
cors 可以分为两种方案进行:
-
第一种方案:cors + 插件配置项
服务器端支持(必须):在跨域的服务器上设置header允许图片跨域 header头应设置为 Access-Control-Allow-Origin: *前端支持(必须):插件配置项 useCORS: true
-
实际项目遇到的问题:
-
第一个问题:后台也配置了二维码的cors,但是canvas上依然画不出二维码。此时你需要跟后台人员确认问题图片存放的cdn地址以及在当前测试环境下图片跨域是否生效,我个人是在测试环境271出问题,但是在预发以及线上环境就没问题了。
-
第二个问题:ios13系统中,第一次在当前页面生成canvas,微信头像会丢失的情况。ios14系统中,初始化微信头像无法显示。
- 排查后发现,因为在HTML中,二维码对应的img标签添加了 crossOrigin='annoymous' 属性。 但是此时又延伸出问题:背景图片跟二维码的img标签如果携带该属性,为什么不出现丢失问题? • 我理解为微信头像 后台跨域配置的问题,因为其他的两张图是在公司cdn的域名下,而微信头像比较特殊,有很多种情况:有微信自己服务器 有公司cdn 同时,各个移动端浏览器厂商对跨域限制的处理不是一致的,所以不好处理。 所以为了统一,还是不加 头像 img 的标签属性 crossOrigin='annoymous'
-
针对第二个问题的 crossOrigin='annoymous'属性 做一个扩展:
• crossorigin - 定义从其他来源的服务器加载资源时使用的选项
• 它有效地更改了浏览器发送的HTTP请求。如果添加了“ crossorigin”属性,则会导致将源:键值对添加到HTTP请求中,如下图所示。
-
英文解释 crossorigin can be set to either “anonymous” or “use-credentials”. Both will result in adding origin: into the request. The latter however will ensure that credentials(凭据) are checked. No crossorigin attribute in the tag will result in sending a request without origin: key-value pair.
-
如果跨域设置不正确,浏览器将会取消该请求
-
第二种方案:把需要跨域的图片,全部转为base64
- 通过canvas转图片为base64的原理:
需要创建具有正确尺寸的canvas元素,并使用drawImage函数复制图像数据。然后,可以使用该toDataURL函数获取数据:具有以64为基数编码的图像的url。请注意,图像必须已完全加载,否则将只获得一个空的(黑色,透明)图像。
• ** 具体实现流程 **
• 初始化拿到图片时, 直接将url转为base64放进src。但这种情况同样需要第一种方案中后端的cors支持,所以还是第一种方案更好。
• 与第一种方案不同的是,
• 插件配置项 useCORS: true 可以不用进行配置了。 配置了也不会受影响。
• 微信头像的img标签设置属性 crossOrigin = 'anonymous' 也不会受影响。
- 转为base64的方案同样用到了canvas。(当然还有很多可以转base64的方法)
- 核心步骤:img.crossOrigin = 'anonymous' 一定不要丢。
getBase64FromImageUrl = (url) => {
return new Promise((resolve, reject) => {
var dataURL = ''
var img = new Image()
img.crossOrigin = 'anonymous'
img.src = url
// Img.src = url + "?timeStamp=" + new Date();
img.onload = function () {
// 要先确保图片完整获取到,这是个异步事件
var canvas = document.createElement('canvas')
var width = img.width
var height = img.height
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(img, 0, 0, width, height)
dataURL = canvas.toDataURL('image/png')
resolve(dataURL)
}
})
}
- 当页面含有多个图片需要转换为base64时,可以使用Promise.all进行处理。
总结
跟后台沟通cors是一切前提。 img标签的crossOrigin='annoymous'属性不能随意设置,必须跟后台沟通cors。解决问题最保险的方式是将图片全部转为base64.
2020 01 20 更新
用cors的方式请求跨域图片跟不用cors的区别。
-
我们首先明确 给img标签加 crossOrigin属性 或者 插件配置项 useCors:true 都会导致cors的触发,区别在于一个是初始化就开始,一个是插件运行时才开始。
-
以两个维度进行展开 :
• 第一个维度,请求头跟响应头的改变 。
• request header的区别 : • 非cors请求:Sec-Fetch-Mode: no-cors • cors请求:Sec-Fetch-Mode: cors ; 新增:Origin: XXX: 什么是Origin:请求从哪里发起的。
- reponse header的区别: 当图片(例如二维码图片)后台配置了跨域后 响应头会增加 核心代码:
Access-Control-Allow-Origin: * (或者 指定域名)。
转载自:https://juejin.cn/post/6920100994026061831