H5本地生成图片分享方案
原先逻辑是通过接口/operational_activity_api/v2/generate_poster 传入URL指定元素转发请求后截图DOM返回图片,缺点十分明显,就是耗时太久。
就拿第一期的连胜打卡举例,我们需要请求一张站内的海报图片以及一张站外附带了二维码的站外海报加图片,在4G情况总耗时约为7s,体验极差。
背景
原先逻辑是通过接口/operational_activity_api/v2/generate_poster 传入URL指定元素转发请求后截图DOM返回图片,缺点十分明显,就是耗时太久。
就拿第一期的连胜打卡举例,我们需要请求一张站内的海报图片以及一张站外附带了二维码的站外海报加图片,在4G情况总耗时约为7s,体验极差。
解决方案
引入html2canvas 库,它的原理大体就是通过递归DOM层层绘制在canvas上。
最终收益
通过本地渲染处Base64后分享,加载两张图片总耗时不需半秒(设备:iPhone 13),具体时间可能会根据设备性能有所不同,大幅提高了用户体验。
核心函数
核心函数localGenareteImage,传入一个DOM或者选择器字符串获得base64
1 | |
这样子其实很简单就可以拿到一个DOM的图片了,但我们的需求通常还有预览、分享站内站外。
实践时细节
以下就拿连胜打卡的场景来说,是通过点击分享按钮 -> 吊起预览图片 -> 点击分享(站内 or 站外)
(以下我将用于渲染图片的 DOM 简称为 renderDOM )
所以这张图片是需要触发了某个事件才开始生成的,也就是说一开始 renderDOM 并不需要展示在页面上,需要通过CSS隐藏。其实我认为 renderDOM 不展示出来的话更灵活,具体按实际场景而定。
隐藏的样式:
( 隐藏DOM的方式很多,但不能直接 display: none,它必须还存在渲染树上才能被获取到样式属性。)
而这个位置,实际上是贴了一个base64出来:
实际结构是这样,.deceit就是 renderDOM,.localImage只是一个展示base64的容器
而为什么不直接展示这个 renderDOM ,是因为我需要渲染两张图片用作站内和站外,而这两张图片的区别只是站外多了一个二维码的部分,所以完全可以复用这个 renderDOM
.deceit实际是这样的,这样子是不是站内只需要渲染上面部分,站外再全部渲染一次就好了?
这样就能拿到两张不同的base64,然后调用原生的分享就好咯
优化

不过这里还能进而再优化一步,就是我们在渲染的时候,红框部分其实被渲染了两次。虽说此时的速度也非常可观了,也能不到一秒就加载完,但这里还是节约一些性能的。
解决方法就是我们只需要渲染红框部分和二维码部分,最后再将它们通过canvas拼接在一起,这样红框部分就只需要渲染一次了。
以下是一个简单的封装,可以将两张图片下上拼接。
1 | |
然后再稍微调整了一下renderDOM的结构,拆成两个部分方便截图就好了。
1 | |
最后,调用原生的分享方法~ 传入站内和站外的base64。
再将base64缓存起来,避免重复渲染重复的图片。
1 | |
注意
如果项目中引用了postcss或使用vw等相对单位,需要改为px这种绝对单位,否则图片会根据设备可视窗口改变宽高,这应该不是我们想要的。
如果使用postcss,可以在postcss.config.js中配置一个selectorBlackList,然后使用对应类名就不会被转换了。
1 | |
html2canvas 的坑
这玩意虽然好使但还是有坑的,以下是我之前开发遇到并解决的问题。
1. 黑边
比如截图出来的图片大概会有1px的黑边,出现的情况是在DOM的背景为渐变的时候,具体原因暂时不明,我的解决方法是通过canvas将底部的黑边裁剪掉,简单粗暴。。
1 | |
2. 背景图片模糊
如果使用了 background: url(…) 这样的属性时,背景图片会出现锯齿。
我的解决方法是通过一个img标签定位在容器上设置 z-index: -1 做背景。
组件封装
暂时存放于keep_and_increase项目下的src/components/sharePoster.vue
template部分
1 | |
localImage用于展示renderDOM生成后的图片,deceit就是存放renderDOM
可以通过debug这个参数来调试renderDOM。
script部分
1 | |
style部分
1 | |
使用示例
1 | |
通过插槽将内容插入组件中,通过get-base64就会返回插槽部分的内容图片base64。
兼容低版本方案
因为连胜打卡是离线包场景,所以不需要考虑低版本的问题。而目前需要复用到非离线包场景,所以考虑到5440前的IOS在调用socialShare时传入base64形式的图片会出现网络错误的情况。为了解决这个问题所以找到服务端在之前webim的上传OSS接口上加了一个类型提供我们上传这个截图,得到一个url路径传递给IOS客户端,这样就不会有问题了。
接口封装
以下是对之前webim的上传OSS接口做了一个简单封装(以下示例中的business为十一周年的)
1 | |
使用示例
从这个文件中导出这个uploadOSS,入参就是一个base64。再将上面组件的实例中再添加一道函数就完事。
1 | |
发现问题
在十一周年活动上线之后观察到话题页有少数用户分享的图片会出现问题
- 截图的字体偏大或字体不统一
- 这样设置本身看到的页面和App就有所差异

- 目前市面上有一些安卓手机能够按比例放大手机内字体的大小,这会导致app、webview中的字体也被强制改变,此时的截图就会有所差异。
- 如果用户手机设置了比较特别的字体也会发生差异。
- 这样设置本身看到的页面和App就有所差异
- 由于浏览器本身就对canvas的尺寸有做限制,比如 Chrome 的限制就是 125w(非官方数据),虽然说这个数值很大,但如果是截取长图,例如 2024.2.5 哑巴开口引流页的聊天记录截图,10 轮对话下来的长度大概就 2000 左右了,这时候一般移动端的页面宽度在 375 左右,最后再为了提高清晰度增加一下缩放倍率2(由于设备像素比的问题,一般都会设置为2来保证一定的清晰度),此时轻易就打破这个限制了。((2000 * 2) * (375 * 2)= 300w)

- 我想到的一个解决思路大概就是将长图分段去截,例如把列表中的每个成员遍历去截,最终按比例缩小逐个绘制在等比例缩放后的画布上。 (失败原因: 亲身体验了一下,因为无论如何想要得到一张高清的长图,分辨率才是至关重要的,所以不管分多少段最终拼接也还需要一张巨大的 canvas 来实现,但这依然会被浏览器限制。)
- 通过服务器用 NodeJS 库 puppeteer 来实现。(需要与服务端商讨)
- 卡顿、内存泄露问题
html2canvas 会加载所有的 dom,过滤不要的 dom,
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const localGenareteImage = (DOMorSelector: HTMLElement | string): Promise<HTMLCanvasElement> => {
const el = typeof DOMorSelector === "string" ? (document.querySelector(DOMorSelector) as HTMLElement) : DOMorSelector;
const width = el.offsetWidth,
height = el.offsetHeight,
canvas = document.createElement("canvas"),
scale = 2; // 放大倍数,提高清晰度,但是会增加大小和性能消耗
canvas.width = width * scale;
canvas.height = height * scale;
const opts = {
useCORS: true,
scale,
canvas,
logging: false,
width,
height,
backgroundColor: "transparent",
ignoreElements: e => {
if (
e.contains(el) ||
el.contains(e) ||
e.tagName === 'STYLE' ||
e.tagName === 'LINK' ||
e.getAttribute('data-html2canvas') != null // header里面的样式不能筛掉
) {
return false;
}
return true;
}
}
return html2canvas(el, opts);
};后续
有时间可以把这个封装成一个组件或一个npm包,可以大幅减少类似场景的开发。补充
使用html2canvas时候,如果截图内包含了img元素,这个img元素需要在url增加时间戳,同时增加允许跨域的参数不然在浏览器测试截图,关闭缓存情况下(disable cache关闭),会出现内存跨域导致img无法生成,加载失效
动态的img
1 | |
一定要有crossorigin=”anonymous”
1 | |
本文标题:H5本地生成图片分享方案
文章作者:Niuhk
发布时间:2024-07-26
最后更新:2025-02-12
原始链接:https://www.niuhk.cn/2024/07/26/H5本地生成图片分享方案/
版权声明:转载请注明出处!
分享