使用html2canvas插件可以无法渲染图片的情况
在使用html2canvas的时候。如果元素中还包含网络图片。那么有很大的几率渲染不出来。即时把html2canvas的允许跨域打开也无济于事。这次就从根源解决这个问题。而且即时渲染出来了。其实同一张图片已经请求了2次(初始渲染一次,html2canvas渲染也请求了一次)。
问题的根本:在插件中,图片请求的时候跨域了(甚至同域的图片都难请求)
更新,把图片放在OSS(云存储)上,oss允许跨域,图片依然是无法生成
解决:在请求对应oss图片的后面,加一个参数,为时间戳,目的就是不使用oss的缓存,就可以解决跨域问题了,如:
https://test.oss.com/image?t=129094392478
解决思路:
1、在JS中使用
Image
对象。该对象可以设置跨域请求。
2、把图片请求回来后直接渲染到canvas
上,注意这里是直接渲染到canvas。并不是通过html2canvas
3、把渲染图片的canvas转成base64的编码格式。
4、把转换好的图片格式重新放到html的元素中
5、使用html2canvas转换对应的html元素
前提条件:
服务器的图片,必须是已经设置了允许跨域的
如果服务器端的图片不允许跨域访问,那么只是前端是很难进行跨域图片处理,以下说的使用img
标签特性也将无效
代码分步实现:
- 先准备需要渲染的列表(这时候注意其实对应的image元素不用传入图片的路径了。避免多次渲染)
<!-- 需要转换的html结构 -->
<div id="test-box">
<!-- 这里不需要把图片路径放入src了。直接通过data存储就行 -->
<img id="img1" src="" data-imgUrl="图片路径.jpg">
<img id="img2" src="" data-imgUrl="图片路径2.jpg">
</div>
// 需要跨域请求的列表
let imgList = [{
id: '#img1', // 图片对应渲染的页面节点
value: $("#img1").data('imgUrl'), // 对应节点的图片(也可以把图片路径直接放这里。因人而异)
}, {
id: '#img2',
value: $("#img2").data('imgUrl'),
}]
- 设置
image
跨域功能 -> 渲染canvas -> 转换为base64 - 19/12/14 更新:
出现 OSS已经允许跨域请求,图片依然报错跨域问题
这个是因为OSS可能会存在一个缓存时间的问题,所以就会引发请求偶尔可以,偶尔又跨域的问题,这时候我们就需要在请求图片的时候加上一个时间戳,防止oss图片缓存问题
/**
* @description: 图片转base64
* @param {String} url 需要转换的图片原链接(http://....jpg)
* @param {String} outputFormat 转换出来的图片的类型(canvas支持jpg/png格式)
* @return: 返回图片对应的base64编码
*/
let getBas64 = (url, outputFormat = 'image/png') => {
return new Promise(function (resolve, reject) {
let canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous'; // 重点!设置image对象可跨域请求
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
let dataURL = canvas.toDataURL(outputFormat);
canvas = null;
resolve(dataURL);
};
// img.src = url; // 旧的方式
img.src = url+'?t='+new Date().valueOf() // 防止oss的缓存问题
})
}
- 最后一步就是拿到
base64
格式的图片。渲染在对应的节点上,渲染完成后用插件转换节点
html2canvas($('#test-box'), {
useCORS: true,
allowTaint: true
}).then(function (canvas) {
ImgUrl = canvas.toDataURL(); // 最后拿到了对应节点生成的图片(base64格式)
})
完整代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- 需要转换的html结构 -->
<div id="test-box">
<!-- 这里不需要把图片路径放入src了。直接通过data存储就行 -->
<img id="img1" src="" data-imgUrl="图片路径.jpg">
<img id="img2" src="" data-imgUrl="图片路径2.jpg">
</div>
<script>
// 需要跨域请求的列表
let imgList = [{
id: '#img1', // 图片对应渲染的页面节点
value: $("#img1").data('imgUrl'), // 对应节点的图片(也可以把图片路径直接放这里。因人而异)
}, {
id: '#img2',
value: $("#img2").data('imgUrl'),
}]
/**
* @description: 图片转base64
* @param {String} url 需要转换的图片原链接(http://....jpg)
* @param {String} outputFormat 转换出来的图片的类型(canvas支持jpg/png格式)
* @return: 返回图片对应的base64编码
*/
let getBas64 = (url, outputFormat = 'image/png') => {
return new Promise(function (resolve, reject) {
let canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous'; // 重点!设置image对象可跨域请求
img.onload = function () {
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
let dataURL = canvas.toDataURL(outputFormat);
canvas = null;
resolve(dataURL);
};
img.src = url;
})
}
// pamoise 初始化请求图片
Promise.all(imgList.map((item) => getBas64(item.value))).then(function (result) {
// 返回的图片列表,重新渲染到页面上
result.forEach((v, k) => {
$(imgList[k].id).attr('src', v);
})
// 赋值完成,生成图片
html2canvas($('#test-box'), {
useCORS: true,
allowTaint: true
}).then(function (canvas) {
console.log(canvas) // 生成的canvas对象
let resImgUrl = canvas.toDataURL(); // 最后拿到了对应节点生成的图片(base64格式)
})
})
</script>
</body>
</html>
- 需要注意的就是调用的循序。示例代码中是进入页面后直接开始这些步骤了。而且必须先请求图片在使用html2canvas插件
- 图片的请求也是异步的。这也就是为什么要用上 Promise 方法。而且该方法适用于需要渲染的节点有多张图片的情况。
这些方法都用上后。图片就只需要在Image
对象请求的时候图片只需要请求一次。避免多次请求
而且亲测百分百渲染 (解决了 跨域图片/网络图片 无法渲染的问题)