canvas 图片合成填坑记
字数 900 / 3 min 读完 / 阅读在实际开发过程中,经常会接到产品关于分享海报,生成海报,照片合成等需求,这里就简单总结一下。关于一次海报合成的填坑过程,欢迎指正。
需求
要用代码来实现多张外部图片和文字的合并而且要上传到七牛云,再将图片链接通过客户端分享出去。图片背景需要支持用户自定义更换。
实现方案
在一个canvas上多次调用drawImage函数,分别绘制在canvas中,多次之后canvas中是多个图片合并的效果,然后再调用toDataURL函数将canvas转成dataURL格式的图片,再将dataURL格式装换为blob文件,上传至七牛云。
需要注意的坑
canvas图片模糊的问题
将canvas的长宽设为样式长宽的2倍或更大, 如下:
var canvas = document.getElementById("myCanvas");
canvas.width = "600";
canvas.height = "600"
canvas.style.height = "300px"
canvas.style.height = "300px"
报安全性错误
如果你的图片url和页面不在同一域下,在调用toDataURL函数的时候就会报安全性错误。chrome中:
Uncaught SecurityError: Failed to execute ‘toDataURL’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported.
Safari中貌似更严格,根域名相同子域不同依然会报错:
Cross-origin image load denied by Cross-Origin Resource Sharing policy.
原因:
没有CORS授权虽然可以在 canvas 中使用图像, 但这样做就会污染(taints)画布。 只要 canvas 被污染, 就不能再从画布中提取数据, 也就是说不能再调用 toBlob(), toDataURL() 和 getImageData() 等方法, 否则会抛出安全错误(security error).这实际上是为了保护用户的个人信息,避免未经许可就从远程web站点加载用户的图像信息,造成隐私泄漏。
解决方法:
为每个图片创建一个新的img对象,再赋给其src等参数,用这种方式要等到img加载完毕再进行canvas其他操作,在img的load事件处理函数中进行操作,否则可能会绘制出空内容。
let img = new Image()
img.setAttribute('crossorigin', 'anonymous')
img.src = imgUrl
img.onload = function () {
// do Something
}
img.error = function () {}
在mac和pc的浏览器上运行正常,但是在ios11以下的手机内无法toDataURL
解决办法:
- 改变图片的载入方式,先在DOM上新建img标签,如下:
<img crossOrigin="Anonymous" id="bgImg" src="https://www.test.com/test.png" alt="">
注意:crossOrigin="Anonymous"
一定要在src
属性前面
被这个坑了很久
- 利用js获取img元素
var img = document.getElementById('bgImg')
ctx.drawImage(img, 0, 0, 700, 700)
canvas.toBlob() safari上无效
解决办法:
将canvas使用toDataURL方法转为base64,再将base64通过file API 转为 blob
function dataURLtoBlob (dataurl) { // 将dataUrl 转为 Blob
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {type: mime})
}
var resultBase64 = canvas.toDataURL('image/jpeg') // 转为jpeg压缩图片大小
var canvasBlob = dataURLtoBlob(resultBase64)
formData 上传方式
var formData = new FormData();
formData.append("file", document.getElementById('file').files[0]);
formData.append("token", token_value); // 其他参数按这样子加入
var xhr = new XMLHttpRequest();
xhr.open('POST', '/uploadurl');
// 上传完成后的回调函数
xhr.onload = function () {
if (xhr.status === 200) {
console.log('上传成功');
} else {
console.log('上传出错');
}
};
// 获取上传进度
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var percent = Math.floor(event.loaded / event.total * 100) ;
// 设置进度显示
$("#J_upload_progress").progress('set progress', percent);
}
};
xhr.send(formData);