Html2Canvas踩坑笔记
简介
Html2canvas主要是将Dom 节点转换成canvas工具库。利用canvas 转换为DataURL 的接口可以实现纯客户端的图片生成以及下载。适用场景主要是一些商品和活动之类的分享海报。
github 仓库
官方文档
1. 优劣
1.1 优势
- 生成图片的过程在客户端,高并发的场景下减少服务器的压力。
- 方案简单开发成本低,
1.2 劣势
客户端的设备各式各样,各种浏览器内核,包括APP 内打开会有很多的兼容性问题。
Html
- Canvas 的兼容问题
- DataUrl的兼容问题
CSS 不支持一下属性
再者Html2canvas 还是预览版,不是很稳定,官方文档上不推荐使用在生产环境
2 . 常见问题
2.1 IOS 13.4 之后无法使用问题
1.0.0 rc 5 版本有问题,需要降级使用1.0.0 rc4
[github issues](https://github.com/niklasvh/html2canvas/issues/2188)
2.2 滚动顶部空白问题
两个解决方案,我使用的第一种,第二种需要滚动页面,体验不好。
- 通过配置设置scroll 位置
{
scrollX: 0,
scrollY: -window.scrollY
}
- 生成图片时,记录下位置,将屏幕滚动到顶部,生成后并恢复到原来位置
window.scrollTo(0, 0)
还有其他方案为实践,比如说降级到1.0.0-alpha.12 ,可能会引起其他bug 不推荐
2.3 图片跨域问题
移动端一般只用开启配置项的useCORS(库会给image添加crossOrigin: Anonymous),即可解决
{
useCORS: true,
}
需要兼容低版本的浏览器的 使用 proxy 配置
2.4 图片加载不全问题
在生成canvas 的时候,html2canvas 会重新加载当前页面的所有静态资源。在网络不好的情况下很容易出现网络图片无法正常生成,在IOS上表现最为明显。原因在于canvas 绘图时,HTTP请求过多,图片未加载完毕。
解决方案: 网络图片转换为Blob本地数据。相当于先把图片下载下来。转换完成后再生成canvas
// 返回图片Blob地址
const toBlobURL = (function () {
const urlMap = {};
// @param {string} url 传入图片资源地址
return function (url: string) {
// 过滤重复值
if (urlMap[url]) return Promise.resolve(urlMap[url]);
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = document.createElement('img');
img.setAttribute('crossOrigin', 'Anonymous');
img.src = url;
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx?.drawImage(img, 0, 0);
// 关键
canvas.toBlob((blob) => {
const blobURL = URL.createObjectURL(blob);
resolve(blobURL);
});
};
img.onerror = (e) => {
reject(e);
};
});
};
})();
// 批量处理
function convertToBlobImage(targetNode: Element | NodeList, timeout: number) {
if (!targetNode) return Promise.resolve();
let nodeList:
| Element[]
| NodeList
| HTMLCollectionOf<HTMLImageElement> = targetNode;
if (targetNode instanceof Element) {
if (targetNode.tagName.toLowerCase() === 'img') {
nodeList = [targetNode];
} else {
nodeList = targetNode.getElementsByTagName('img');
}
} else if (!(nodeList instanceof Array) && !(nodeList instanceof NodeList)) {
throw new Error('[convertToBlobImage] 必须是Element或NodeList类型');
}
if (nodeList.length === 0) return Promise.resolve();
// 仅考虑<img>
return new Promise((resolve) => {
let resolved = false;
// 超时处理
if (timeout) {
setTimeout(() => {
if (!resolved) resolve();
resolved = true;
}, timeout);
}
let count = 0;
// 逐一替换<img>资源地址
for (let i = 0, len = nodeList.length; i < len; ++i) {
const v: any = nodeList[i];
let p = Promise.resolve();
if (v.tagName.toLowerCase() === 'img') {
p = toBlobURL(v.src).then((blob) => {
v.src = blob;
});
}
p.finally(() => {
if (++count === nodeList.length && !resolved) resolve();
});
}
});
}
export default convertToBlobImage;
2.5 图片下载问题
最简单的方式就是拿到html2canvas的返回结果canvas 转换为DataURL
const imgData = canvas.toDataURL('image/jpeg')
2.5.1 a 标签下载
可以动态的设置文件名称。
base64长度过长会引起下载失败(解决方案是转为blob 对象,待验证)
根据之前的经验blob 也会存在一些兼容问题。
第一是 大文件的blob,这个可以忽略(500M - 800M) 图片来源filesave.js
第二个 就是Html API 的一些兼容问题
2.5.2 长按下载
把imgData
的 赋值给img
标签的src 属性,下方添加长按保存图片
文案提示
默认名称为下载.jpg
下载兼容性测试
浏览器 | 平台 | 长按 | a标签 |
---|---|---|---|
UC | IOS | √ | x |
UC | 安卓 | √ | x |
360 极速 | IOS | x | x |
360 | 安卓 | √ | x |
QQ浏览器 | IOS | √ | x |
QQ浏览器 | 安卓 | √ | x |
Safrai | IOS | √ | √ |
夸克 | IOS | √ | - |
夸克 | 安卓 | √ | - |
搜狗 | IOS | √ | x |
搜狗 | 安卓 | √ | x |
Chrome | IOS | √ | √ |
Chrome | 安卓 | √ | √ |
samsung | 安卓 | √ | √ |