随着 APP 的获客成本越来越高, 很多产品开始从 wap 页引流, 而最常见的方式便是分享, 尤其是在微信中. 因此诞生了一些新玩法, 比如生成一张海报图片, 用户可以保存或分享到其他平台.
本文将介绍如何生成一张海报图片, 以及可能会碰到的问题和解决方案.
canvas 转图片
目前移动端浏览器对于 canvas 的支持非常好, 而 canvas 可以通过 toDataURL 来转换成 base64 图片.
市场上的一些 js 库, 如: https://html2canvas.hertzen.com/ , https://github.com/tsayen/dom-to-image , 其本质也是通过 toDataURL 来转换成图片. 但我个人不太建议使用这类 js 库, 因为你可能需要填很多坑, 结果也并不一定能达到你的期望, 所以还是老老实实用 canvas 画出来吧.
外链图片
最重要也是最复杂的便是对外链图片的处理, canvas 绘图时不会有任何问题, 但是调用 toDataURL 这个方法时, 浏览器会报错.
跨域
你请求的外域图片, 可能会暴露你的隐私, 所以浏览器为了保护你的隐私限制了这样的请求.
我们可以设置 crossOrigin 为 anonymous 来允许跨域, 浏览器会为这张图片的请求头附带 Origin 信息, 告诉静态资源服务器, 请在响应头中附带
- Access-Control-Allow-Methods
- ,
- Access-Control-Allow-Origin
, 以便浏览器放行.
但是有些时候, 设置了 crossOrigin 依然会报错, 其实不是设置了没有作用, 而是 cdn 缓存了服务器响应结果, 这个结果往往是没有上述两个字段的. 这个时候可以考虑给图片链接后追加时间戳, 对于 cdn 来说, 这是一个没有请求过的资源, 因此它会从源服务器去拿数据.
代码示例如下:
- var img = new Image()
- img.crossOrigin = 'anonymous'
- img.onload = function () {
- // 在图片加载完成后绘图, 避免空白和断断续续加载
- ctx.drawImage(img, 0, 0)
- }
- img.src = 'https://xxxx' + '?' + (+new Date())
保存图片
a 标签有一个 download 属性, 可以将指定的资源下载下来, 但该方法只适用于 pc 端, 移动端基本不支持 (Safari 会打开一个 base64 的网页, 而在微信中甚至不会有任何响应, 更不用提众多的安卓机).
既然不能在浏览器主动保存图片, 我们只好另辟蹊径, 经调研发现: 现在绝大多数的移动端浏览器都支持长按图片唤起下拉菜单来保存, 因此我们可以通过文案提示用户进行操作, 但它的弊端是没有 API 来调用, 也就是说只能提示用户自发地进行长按保存操作, 而我们对于用户是否保存了图片是无感知的.
文末总结
对于目前的产品需求, 长按图片保存基本能够满足要求. 所以, 目前的做法是: 通过 canvas 绘制一张海报图片, 将其转换为 base64 图片, 创建 img 标签并渲染到视图上, 文字提示用户长按可以保存 (只有在图片上长按才有效).
示例代码:
- var canvas = document.getElementById('canvas')
- var ctx = canvas.getContext('2d')
- canvas.width = 300
- canvas.height = 300
- // 画圆
- ctx.fillStyle = 'yellow'
- ctx.fillRect(0, 0, 50, 100)
- ctx.strokeStyle = 'blue'
- ctx.strokeRect(0, 120, 50, 100)
- var img = new Image()
- img.onload = () => {
- // 画图片
- ctx.drawImage(img, 60, 0)
- // toImage
- var dataImg = new Image()
- dataImg.src = canvas.toDataURL('image/png')
- document.body.appendChild(dataImg) // 长按图片保存
- }
- img.crossOrigin = 'anonymous'
- img.src = 'https://nos.netease.com/easyread/fle/a0df1d4009c7a2ec5fee/1524215500140/avatar.png?' + (+new Date())
来源: https://juejin.im/post/5aefc0456fb9a07abc29d36d