前记
很久之前为了完善运行时问题反馈体系开发了用户声音相关功能,其中有一个模块是截图生成当前图片并上传,作为一个开发老鸟,必须canvans搞一搞,找了现成的一套插件htmlcavans直接引入,这不是so easy,最终实现之后发现还是自己太年轻了,各种深渊大坑,特此记录一下踩坑之路。
问题
- 1、生成图片的时候img标签生成失败显示空白
- 2、弹出组件截图背景纯黑色或纯白色
- 3、iframe生产失败,截图之后iframe区域显示空白
下面一一分析解决思路
生成图片的时候img标签生成失败显示空白
按照网上教程对htmlcavans进行各种设置,均未能解决该问题。所以剑走偏锋,在生成canvans的时候copy一份dom出来,然后将该dom下的img标签增加一个backgroundImg属性,copy一份的原因是防止更新影响原先视图,控制影响范围,下面直接上代码
let cloneDome = document.cloneNode(true);
let ImgList = cloneDome.querySelectorAll("img");
//设置背景为实现截图
for (let i = 0; i < ImgList.length; i++) {
let src = ImgList[i].getAttribute("src");
ImgList[i].style.backgroundImage = ` url(${src})`;
ImgList[i].style.backgroundSize = "100%";
}
return new html2canvas(cloneDome.body, {
....
})
弹出组件截图背景纯黑色或纯白色 (慎用!)
这个问题也困扰了一段时间,经过排查是对opacity属性支持有问题导致,处理方案在生成图片是同样对copy的dom进行操作,转换为rgba背景色实现类似透明度效果,不过该方案可能存在误杀谨慎使用
let cloneDome = document.cloneNode(true);
//由于批量处理存在风险,此处只处理需要处理的dom
if(cloneDome.querySelector("******")){
cloneDome.querySelector("******").style.opacity = 1;
cloneDome.querySelector("******").style.backgroundColor =
"rgba(0,0,0,0.5)";
return new html2canvas(cloneDome.body, {
....
})
}
iframe生产失败,截图之后iframe区域显示空白
iframe生成图片失败,一开始我们认为是domain不同导致的跨域问题,但是调整domin之后依然失败,问题又回来了,和组大牛讨论之后决定采用一个比较骚的套路具体实现是:在生成图片之前如果存在iframe则主页面给iframe发送消息,在iframe内自截图生成图片并传递给主页面,主页面合并图片,但是合并图片存在一些问题,最棘手的就是合并的位置如何计算获取,转念一想没必要去自己计算直接给iframe设置背景图片就完美的解决了最棘手的合并位置问题,代码如下:
(ps:该方案默认只存在一个iframe下,多个iframe由于性能等问题暂不考虑,多个iframe支持情况可以自行修改代码)
//如果存在ifram======等待生成ifram的图片
let ifram = cloneDome.querySelector("iframe");
let promiseProxy=""
if (
ifram &&
ifram.getAttribute("src").indexOf("fas.teld.org") != -1
) {
promiseProxy = new Promise((resolve)=>{
window.onmessage = (e) => {
if (e.data.type == "ScreenshotResult") {
ifram.style.backgroundSize = "100%";
ifram.style.backgroundImage = ` url(${ e.data.data})`;
resolve()
}
};
//消息失败超时自动放行,防止页面卡死
setTimeout(() => {
resolve()
}, 10000);
})
ifram.contentWindow.postMessage({ type: "Screenshot" }, "*");
}
if(promiseProxy){
await promiseProxy
}
return new html2canvas(cloneDome.body, {
....
})