HTML2canvas截图 解决 canvas 将图片转为base64报错 Uncaught DOMException Failed to execute 'toDataURL' on 'HTMLCanvasElement' Tainted canvases may not be exported

最近在做一个分享生成海报的功能,用户进到页面,开始根据用户信息展示html网页,然后用html2canvas生成图片,用户长按图片保存到本地的功能

1、html2canvas 官网
html2canvas的详细介绍可以点击这里查看,其实简单来说就是通过canvasHTML生成的DOM节点绘制到画布上,再可以通过自己的需求转换成图片.所以官方文档也说了,最后生成的效果不是100%相同的,这一点大家要有心理准备,无论怎样,一点点小瑕疵是肯定有的。

2、Code to Image github地址

3、dom-to-image 官网

html2canvas

html代码

1
<img src=""  crossorigin="Anonymous"></img>

js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
document.querySelector(".main-picture")  中 main-picture=>是你要截图的最外层的选择器

html2canvas(document.querySelector(".main-picture"),{

useCORS:true,//设置可以素材可以跨域,移动端不兼容

allowTaint: false,//默认就是false,允许跨域

taintTest: true,//默认就是false,是否在渲染前测试图片

scale:window.devicePixelRatio,//解决清晰度的问题可以改变scale的值

}).then(canvas =>

var dataUrl = canvas.toDataURL();

 var newImg = document.createElement("img");

newImg.src =  dataUrl;

document.body.appendChild(newImg);

});

但是发现一个问题,就是背景图片,或者说所有的图片都没有被截图截上,原因:

2、关于图片需要截图,所有的元素需要动态创建元素document.createElement('img') 这样图片就可以截图截出来了关于这一点,我可能被误导了,因为html2canvas里面有这段动态创建的代码,为什么我当时跨域也设置了但是没有截到加入的图片是因为,图片是一个静态资源,我通过nginx或者node服务器将资源指向本地方便测试,因为资源指向本地,而本地的nginx以及node配置的服务器上面没有配置header头加的access-control-allow-origion:*,所以产生了误解要动态创建

遇到的问题

跨域问题
就是背景图片,或者说所有的图片都没有被截图截上,原因:

如果页面上有跨域的图片需要做处理,同事html2canvas需要传允许跨域的字段,后端也要加允许跨域才可以
WechatIMG11.jpeg

1
2
3
4
5
6
7
8
9
     html2canvas(this.imgRef.current, {
useCORS: true // 开启跨域设置,需要后台设置cors
}).then((canvas) => {
// toDataURL函数生成img标签的可用数据 图片格式转成 base64
let dataURL = canvas.toDataURL("image/png")
console.log(dataURL)
})
<img crossOrigin='anonymous'
src="https://xxx.com/a.jpeg" className="w100" alt=""/>

这是为什么呢?
因为canvasHTML生成的DOM节点绘制到画布上,然后用canvas.toDataURL导出图片,如果是读图片内容,是有浏览器同源限制,toDataURL是读取图片

2、返回图片是base64即可

接下来纯canvas绘制遇到的问题

用html2canvas转base64 ,但是在图片转base64的过程中遇到了两个问题

  • 1、图片无法绘制,转成的base64 用浏览器打开是空的透明画布,如图

image

代码片段如下:

1
2
3
4
5
6
7
8
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64

img.src = 'http://www.xxxx.png';
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");

这个时候我想到问题应该是 图片还没加载完毕 就已经绘制了,既然是这样,那么修改为以下:

1
2
3
4
5
6
7
8
9
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64
img.src = 'http://www.xxxx.png';
img.onload = function(){//图片加载完,再draw 和 toDataURL
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");
};

修改完毕我正要打算喝杯水庆祝一下,一刷新页面,一口老血喷了出来,chrome控制台又报错如下:

1
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

大概意思是canvas无法执行toDataURL方法:污染的画布无法输出,请原谅我的灵魂翻译。
经google 发现原来是受限于 CORS 策略,会存在跨域问题,虽然可以使用图像(比如append到页面上)但是绘制到画布上会污染画布,一旦一个画布被污染,就无法提取画布的数据,比如无法使用使用画布toBlob(),toDataURL(),或getImageData()方法;当使用这些方法的时候 会抛出一个安全错误,读图片内容,是有同源限制,canvas的drawimage应该是绘制,toBlob(),toDataURL(),或getImageData()读取图片内容

1
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

具体详情附上一个链接非常详细如下:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image,非常值得一看,个人理解可能不到到位,还是建议读读这个链接。

解决方案:

图片设置 :crossOrigin属性
代码片段:img.setAttribute(“crossOrigin”,’Anonymous’)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
``
var canvas=document.getElementById("canvas"),//获取canvas
ctx = canvas.getContext("2d"), //对应的CanvasRenderingContext2D对象(画笔)
img = new Image(),//创建新的图片对象
base64 = '' ;//base64
img.src = 'http://www.xxxx.png';
img.setAttribute("crossOrigin",'Anonymous')
img.onload = function(){//图片加载完,再draw 和 toDataURL
ctx.drawImage(img,0,0);
base64 = canvas.toDataURL("image/png");
};

stackoverflow上解决方案:
地址:https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror

Tips:如果遇到其他canvas 关于SecurityError 的问题,也可以尝试一下这个解决方法。
另外经过多次搜索,总结了几个小窍门

1:使用google 。baidu搜索的都是转来转去的几篇文章,干扰太大。
2:首先搜索bug之家 :stackoverflow,总有解决你的bug的方案,链接:https://stackoverflow.com/
3:web文档之家:mozilla的web文档 ,非常权威,非常详尽。链接:https://developer.mozilla.org/en-US/

html2canvas在微信浏览器中遇到的问题

微信浏览器中截不了图不会走then函数中

在真机ios的微信浏览器中截图会有问题,截不了图,这个时候将html2canvas版本回滚到1.0.0-rc.4即可,如果不行,记得清除微信的缓存再试试

1
“html2canvas”: “1.0.0-rc.4”
html2canvas截图不全的问题

image.png
原因:
我所知道的原因有两点,

第一点:在点击保存图片时,此时要保存的资源较多,造成模块并没有完全加载完毕,就已经生成了截图;
解决方案:(加上一个延时操作)

1
2
3
4
5
6
// 利用 html2canvas 下载 canvas
setTimeout(() => {
html2canvas(img, { canvas: canvas }).then(function(canvas) {
_this.photoUrl = canvas.toDataURL();
});
}, 500);

第二点:滚轮滑动造成的,主要是html2canvas是根据body进行截图,若内容高度高于body时,就会出现这样的问题(大概意思就是有滚动条时造成的)
解决方案:body内加一层div设置高度是100vh超出滚动,这样就不会高于body了