关于html2canvas的问题记录

1,797 阅读6分钟

前言

最近公司新提一个转介绍的需求,主要是为了通过客户进行引流。需求比较简单,就是在企微侧边栏展示一张成员相关的名片,然后销售可以在合适的时候发送给客户进行引流。名片内容是一张宣传图+成员昵称头像+一张成员二维码,整体内容就是这样

                              

接到需求的时候,我就大概分析了一下这个需求,然后发现以自己的水平很难纯手写实现,然后立马就去百度参考了一波,发现了两种方案 html2canvas & puppeteer,综合自己的实际情况(就是菜),选择html2canvas来完成需求,结果在开发过程中遇到一些问题,所以先记录下来,以免以后再次遇到

html2canvas

html2Canvas据说是一个成熟的开源库,能将DOM直接转为一张图片,它基于DOM生成,并不是实际性的屏幕截图,因此多少有些误差,比如生成的图片模糊、生成的图片会偏移(图片左边和顶部会有大概5像素的白边)等等。

html2canvas使用也相当简单,install就不提了,大家都会,我们主要看使用代码

import html2canvas from 'html2canvas';
html2canvas(dom as HTMLElement, {}).then((canvas) => {
        //获得图片的64位编码
        let dataURL = canvas.toDataURL("image/jpeg");
      });

主要代码只有这么多,使用超级简单,同时html2canvas提供了几个比较有用的配置项

弄好之后我用本地图片进行了一波测试,DOM很成功的被转换成了一张base64位的图片,整个过程出乎意料的顺利,顺利到让我以为这就完事了,就把它放到一边了,等到开始其他都完成了准备调试接口的时候,才发现还有一些问题没解决

问题总结

1、跨域

根据需求,我们需要在后管创建一个模板,然后再企微侧边栏选择相应模板生成名片,我们的图片一般是放在OSS上的,这样我们可以直接加载OSS图片进行显示。

等我使用了服务端返回来的OSS地址之后,才发现竟然跨域了!!

遇到跨域问题,首先肯定是要问下自己的后端队友是不是做过跨域的配置,于是去找了后端问了问,结果由于沟通问题,他让我以为这张图片是从企微的OSS返回来的,我寻思企微应该不至于会出现这样的问题,那么思路就转向了本地配置。

html2canvas本身是不支持跨域图片的,所以我们需要做一些配置,如下:

let options = {
useCORS: true,
}
html2canvas(dom as HTMLElement, options).then((canvas) => {
        //获得图片的64位编码
        let dataURL = canvas.toDataURL("image/jpeg");
      });

重点就是 useCORS: true ,它可以尝试使用CORS从服务器加载图像,抱着一丝希望加上去运行之后发现还是报错,然后我又去查找了一下其他的博客, 又发现还有其他的配置

<img
id="load-page"
crossOrigin="anonymous"
:src="imgUrl">

PS: 有人说要给url后面加上随机的字符串,还有人说要固定的字符串(这个配置我没试)。

总之加了crossOrigin还是不能解决问题

抱着解决问题的心态去查,结果无一例外都是固定的套路 useCORS + crossOrigin ,他们都可以解决问题而我这里不行,让我不禁怀疑我是否适合这个职业。

正当我头大这个问题的时候,转机突然就来了

中午和后端出去吃饭的时候,意外得知这是我们的OSS服务器,我立马反问上次你不是说这是企微的吗,他很惊讶我啥时候说过?

WTF.....

结果下午后端配合加上跨域配置之后图片顺利加载,整个过程不超过10分钟,问题完美解决。

PS:别的收获没有,唯一一点就是一定要有良好的沟通能力,不然自己把自己坑死

2、图片偏移、模糊

这个问题在图片加载后出现,展现效果就是生成的图片整体偏移,左边和上边会出现大概5px的白边。

这个问题倒是不难解决,有些人说可以通过设置相对视窗的偏移值,让图片复位就可以了

 // 获取元素相对于视窗的偏移量
var rect = (dom as HTMLElement).getBoundingClientRect();
//设置context位置,值为相对于视窗的偏移量负值,让图片复位context.translate(-rect.left, -rect.top); 

很遗憾,设置了没效果

后来我自己尝试了一下,在配置里面加了

x: rect.left + 5,
y: rect.top + 5,

解决掉了,但是我感觉这样好像不太对劲,但是又说不上来哪里不对,总之问题解决。

至于图片模糊的问题,我在配置项中加了scale,最终解决了,最起码生成的图片中二维码很清晰,很轻松的可以扫描出来。

到这里,基本上已经结束了,虽然感觉没有遇到非常困难的问题,但是因为学前端开发也就半年不到,底子没打好,做事情总会困难一点,不过结果是好的,也很开心了。

最终

附带一下我的所有配置项吧,希望能够帮助到其他伙伴

const loadDataUrl = () => {
var dom = document.querySelector("#refLayout");
const box = window.getComputedStyle(dom as HTMLElement);
const width = parseInt(box.width, 10);
const height = parseInt(box.height, 10);
var scaleBy = 2; //缩放比例
var canvas = document.createElement("canvas");
// 获取元素相对于视窗的偏移量
var rect = (dom as HTMLElement).getBoundingClientRect();
console.log(rect);
canvas.width = width * scaleBy;
canvas.height = height * scaleBy;
canvas.style.width = width * scaleBy + "px";
canvas.style.height = height * scaleBy + "px";
var context = canvas.getContext("2d");
//@ts-ignore
context.scale(scaleBy, scaleBy);
//@ts-ignore
context?.translate(-20, -4.4);
// context.translate(-rect.left, -rect.top); //设置context位置,值为相对于视窗的偏移量负值,让图片复位
html2canvas(dom as HTMLElement, {
//@ts-ignore
dpi: window.devicePixelRatio * 2,
// scrollX: -4.4,
// scrollY: -4.4,
x: rect.left + 5,
y: rect.top + 5,
width: width,
height: height,
scale: scaleBy,
canvas: canvas,
backgroundColor: "#ffffff",
useCORS: true,
}).then((canvas) => {
//base64位图片编码
let dataURL = canvas.toDataURL("image/jpeg");
//针对图片进行处理,转为file上传至服务端
});
};               

总结

沟通很重要,非常重要,异常重要

另外,因为我本身学前端不久基础比较差,所以有些问题一直没能解决,我想在这里问一下大家,希望有小伙伴可以帮到我

我对前端的兼容适配一直搞不明白,就像不同浏览器之间以及PC-移动端之间的适配,一直不知道怎么弄。

还有JavaScript的一些方法可以在那里学习到,每次写代码总是很臃肿,希望能够找到系统学习的思路

如果有小伙伴知道的话,还是希望能够帮我解惑,谢谢啦。