近期接到几个功能类似的需求,都是js处理图片相关的,在开发过程中踩了一些坑,现在把过程记录下来。
第一个需求是后端返回包含底图和二维码的数据列表,要将二维码绘制到底图对应的区域,图片使用canvas来进行绘制
首先创建画布
var width = $('#img_list li').width()
var height = $('#img_list li').height()
var canvas= document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.rect(0, 0, canvas.width, canvas.height);
context.fill();
这里就踩了一个坑,因为 $('#img_list li')
是从后端获取数据之后循环的,所以在一开始是无法获取到宽高的,当时绘制完成列表上怎么都不出来图片,后面才发现这个问题,所以在创建画布的时候一定要保证宽高是有值的。
下面是将图片绘制到画布上,这里不能直接使用图片,要先new图片对象,在对象的onload事件里进行绘制,
var myImage = new Image();
myImage.setAttribute('crossOrigin', 'anonymous')
myImage.src = image;
myImage.onload = function () {
context.drawImage(myImage, 0, 0, width, height);
var myImage2 = new Image();
myImage2.setAttribute('crossOrigin', 'anonymous')
myImage2.src = qrcode;
myImage2.onload = function () {
var x = width * 0.205
var y = height * 3.39
var w_ = height / 8
var h_ = height / 8
context.drawImage(myImage2, x, y, w_, h_);
}
}
context.drawImage(myImage, 0, 0, width, height)
是将图片在canvas上进行绘制,方法的五个参数分别是(绘制的文件,绘制起点在画布的X轴坐标,Y轴坐标,绘制图片的宽,高)。
这里又有一个坑,如果图片是非本地图片,将会出现图片跨域的情况不能直接使用,首先要后端先配置好允许图片跨域,我这边是后端给nginx加跨域处理,然后前端需要加
myImage.setAttribute('crossOrigin', 'anonymous')
现在已经绘制完成了,但是现在是canvas,用户不能下载,需求是要用户能下载合成之后的图片,那就将canvas转成base64,再赋值给图片的URL。
var base64 = canvas.toDataURL("image/png");
var img = document.getElementById('avatar');
img.setAttribute('src', base64);
现在就可以进行下载了。
第二个需求是前端生成带logo的二维码,这次的底图是固定的所以就存在前端,不会存在图片跨域的问题,绘制方法跟前面一样,所以只考虑生成二维码的问题,二维码的生成工具很多,但大都不能带二维码,本次生成使用了 jquery.qrcode.js
。
$("#qrcode").qrcode({
render: "canvas",
text: val, // 二维码携带的参数
width : "180", // 二维码的宽度
height : "180", // 二维码的高度
background : "#ffffff", // 二维码的后景色
foreground : "#000000", // 二维码的前景色
quiet: 1, // 边距
mode: 4,
mSize: 11 * 0.01,
mPosX: 50 * 0.01,
mPosY: 50 * 0.01,
image: $('#logo')[0] // 二维码中间的图片
});
二维码生成完成,然后就是图片合成以及canvas转图片下载,文章前面已经有记录就不在重复。
第三个需求是OCR车牌识别功能,客户在C端进行拍照上传识别车牌号码,现在市面上的手机像素都很高,一张照片小则4、5M,大则10+M,在上传的时候比较慢而且浪费流量,OCR接口也要求图片必须小于3M,所以压缩图片就是非常必要的操作了,压缩还是使用canvas。
let formData = new FormData()
let files = this.$refs.uploadFile.files
let size = files[0].size / 1024 / 1024
根据size来判断大小,小于3M直接走上传流程,大于3M就进行压缩。
let divisor = Math.ceil(size / 3) // 文件大于3M,向上取整,倍数压缩
var oFile = files[0];
var reader = new FileReader();
reader.onload=function(){
var oImg = new Image();
oImg.src = this.result;
oImg.style.display = "none"
document.body.appendChild(oImg);
oImg.onload = function(){
var imgWidth = this.width
var imgHeight = this.height
var w = imgWidth / divisor;
var h = imgHeight / divisor;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.fillRect(0, 0, w, h);
ctx.drawImage(oImg, 0, 0, w, h);
const base64 = canvas.toDataURL('image/jpeg', 0.75); // 压缩后质量
that.uploadFile(that.dataURLtoFile(base64)) // base64转文件 - 然后上传
};
};
reader.readAsDataURL(oFile);
dataURLtoFile (dataurl) { // base64转文件
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `file.${suffix}`, {
type: mime
})
}
最后将base64转回文件方便上传。