1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 前端图片压缩 方向纠正 预览 上传插件

前端图片压缩 方向纠正 预览 上传插件

时间:2022-07-01 05:03:09

相关推荐

前端图片压缩 方向纠正 预览 上传插件

原文链接: https://mp./s/_erabW9UbiaIshH8YIR1Ow

前言

今天我们要讲解下如何编写一个图片压缩、方向纠正插件,附带着会讲解下如何上传和预览。

为什么重点放在图片压缩和方向纠正?

相信大家在做项目过程中,经常会遇到上传图片到后端,但是由于图片过大,需要对图片压缩处理。特别在移动端,手机拍的照片普遍过于大了,我们有时候只是需要上传一张头像,很小就够用了。还有在部分手机上(已知苹果手机)拍的照片存在方向角度问题,这时就需要我们来纠正图片角度了。

很多同学多数时候是在用别人写好的图片压缩上传插件。针对我们的需求,这些插件有时候不能达到我们最理想的效果,自己写呢,又不会写,很是头疼。今天就深入剖析讲解下,教会大家编写自己的图片压缩、方向纠正插件,以及预览和上传压缩后图片数据。

文中用到的一些H5的api和EXIF.js等知识点如果不懂的话,请先阅读文末尾的结语中的知识点资料。

实现原理

压缩图片并且上传主要用到filereader、canvas 以及 formdata 这三个h5的api和插件EXIF.js。逻辑并不难。整个过程就是:

(1)用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式)

(2)把图片数据传入img对象,然后将img绘制到canvas上,用EXIF.js对图片方向进行纠正,再调用canvas.toDataURL对图片进行压缩,获取到压缩后的base64格式图片数据,转成二进制

(3)获取到压缩后的图片二进制数据,预览。

(4)将压缩后的图片二进制数据塞入formdata,再通过XmlHttpRequest提交formdata

如此四步,就完成了图片的压缩、方向纠正、预览和上传。

插件设计思考

考虑到在实际项目中,可能用不同的开发框架(vue.js/JQ/react.js/angular.js/anu.js等),图片预览的UI样式也可能不同,图片数据上传方法可能不同。因为图片压缩和方向纠正这两块的逻辑多变性比较低,我们这里把图片压缩和方向纠正抽离出来,封装为一个插件库。

【一】获取图片数据

先是获取图片数据,也就是监听input file的change事件,然后获取到用来压缩上传的文件对象files,将files传到【图片压缩、方向纠正插件】中进行处理。

这时候根据每个人的需求,也可以预览未压缩的图片。

//监听上传组件input的onchange事件,压缩图片,纠正图片方向,同时获取压缩后的图片filechooser.onchange=function(){varfileList=this.files;//预览压缩前的图片varfiles=Array.prototype.slice.call(fileList);files.forEach(function(file,i){varreader=newFileReader();reader.onload=function(){varli=document.createElement("li")li.style.backgroundImage='url('+this.result+')';document.querySelector('.img_list').appendChild(li)}reader.readAsDataURL(file);});//处理图片列表,getCompressiveFileList接受处理后的图片数据列表//下面两行代码为图片压缩、方向纠正插件的用法,具体实现细节请继续往下阅读~_~↓↓↓varprocess=window.lzImgProcess();process(fileList,getCompressiveFileList);}

【二】图片压缩、方向纠正插件实现

上面做完图片数据的获取后,就可以做process压缩图片的方法了。而压缩图片也并不是直接把图片绘制到canvas再调用一下toDataURL就行的。

在IOS中,canvas绘制图片是有两个限制的:

首先是图片的大小,如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据。

再者就是canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的。

应对上面两种限制,我把图片宽度、高度压缩控制在1000px以内,这样图片最大就不超过两百万像素了。在前端开发中,1000px*1000px基本可以满足绝大部分的需求了。当然了还有更完美的瓦片式绘制的方法,我们这里就说瓦片式绘制方法了。

如此一来就解决了IOS上的两种限制了。

除了上面所述的限制,还有两个坑,一个就是canvas的toDataURL是只能压缩jpg的(这句话的详细解释可以看下面的Tip讲解),当用户上传的图片是png的话,就需要转成jpg,也就是统一用canvas.toDataURL('image/jpeg', 0.5) , 类型统一设成jpeg,而压缩比就自己控制了。

另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就是绘制之前在canvas上铺一层白色的底色。

在压缩图片之前,我们判断图片角度,如果图片角度不正确,还需要用EXIF.js把图片角度纠正过来。

压缩完图片,把base64的图片数据转成二进制数据存储到暂存区中,等待被getBlobList获取使用。

Tip:canvas的toDataURL是只能压缩jpg这句话我可能说的不清楚,我想表达的意思是这个api无论是jpeg还是png,最后导出的时候,都跟jpeg没啥差别了。因为png图片的透明性质在canvas中是无效的,会被canvas添加默认的黑色背景,我在文中讲解的时候,用白色背景处理了,所以最后导出的图片,无论你设置的是png还是jpeg都跟jpeg没啥区别了,因为无法保持png的透明度性质了

(function(window){/****作者:混沌传奇**邮箱地址:iot-pro_lizeng@**日期:-10-26**插件功能:压缩图片&&纠正图片方向&&返回二进制(Blob)图片元数据组成的列表**/window.lzImgProcess=function(){varOrientation='',//图片方向角blobList=[],//压缩后的二进制图片数据列表canvas=document.createElement("canvas"),//用于压缩图片(纠正图片方向)的canvasctx=canvas.getContext('2d'),file_type='image/jpeg',//图片类型qlty=0.5,//图片压缩品质,默认是0.5,可选范围是0-1的数字类型的值,可配置imgWH=1000;//压缩后的图片的最大宽度和高度,默认是1000px,可配置/***@actionNameprocess,*方法功能:压缩图片&&纠正图片方向&&返回二进制(Blob)图片元数据**@paramfileList,传入函数的文件列表对象,fileList对象是来自用户在一个<input>元素上选择文件后返回的FileList对象*注意:图片类型必须是jpeg||png*比如:<inputid="uploadImage"onchange="loadImageFile();"/>*functionloadImageFile(){*//获取返回的fileList对象*varfileList=document.getElementById("uploadImage").files;*}*@paramgetBlobList[Blob],获取压缩结果的钩子函数,接受一个参数。*功能:在图片压缩完毕后,获取压缩后的二进制图片数据对象组成的数组,参数即:压缩后的二进制图片数据(blob)组成的list**@paramquality,传入函数的图片压缩比率(品质),可选范围0-1的数字类型的值,默认是0.5**@paramWH,传入函数的图片压缩后的最大图片宽度和高度,默认是1000,单位是px,可自由配置。*注意:最好不要超过1000,数字过大,容易导致canvas压缩失败。由于没做瓦片处理,所以有这个限制。1000*1000的图片在前端中,基本也够用了。**/functionprocess(fileList,getBlobList,quality,WH){blobList=[];//初始化blobList//判断参数fileList的长度是否大于0if(!fileList.length){console.log('警告:传进方法process的参数fileList长度必须大于零!!!')return;}//如果quality参数有值,则把quality赋值给qlty(图片压缩的品质)if(quality)qlty=quality;//如果WH参数有值,则把WH赋值给imgWH(压缩后的图片的最大宽度和高度)if(WH&&WH<1000&&WH>0){imgWH=WH;}//把传进来的fileList转为数组类型varfiles=Array.prototype.slice.call(fileList);files.forEach(function(file,i){if(!/\/(?:jpeg|png)/i.test(file.type)){console.log('警告:图片必须是jpeg||png类型!!!');return;}//file_type=file.type;varreader=newFileReader();//获取图片压缩前大小,打印图片压缩前大小varsize=file.size/1024>1024?(~~(10*file.size/1024/1024))/10+"MB":~~(file.size/1024)+"KB";//console.log('size:',size)reader.onload=function(){varimg=newImage();img.src=this.result;//图片加载完毕之后进行压缩if(plete){callback();}else{img.onload=callback;}functioncallback(){//获取照片方向角属性,用户旋转控制EXIF.getData(img,function(){//alert(EXIF.pretty(this));EXIF.getAllTags(this);//alert(EXIF.getTag(this,'Orientation'));Orientation=EXIF.getTag(this,'Orientation');console.log('Orientation:',Orientation)if(Orientation==""||Orientation==undefined||Orientation==null){Orientation=1;}});//获取压缩后的图片二进制数据vardata=GetImgCompress(img);//将二进制数据塞入到二进制数据列表中blobList.push(data);//将压缩后的二进制图片数据对象(blob)组成的list通过钩子函数返回出去if(blobList.length===files.length){if(getBlobList)getBlobList(blobList);}img=null;}};reader.readAsDataURL(file);})}/***@actionNameGetImgCompress,*功能:判断上传图片的方向,如果不是正确的,进行修正,并对图片进行压缩,压缩完后,返回压缩后的二进制图片数据**@paramimg,用来压缩的图片对象**@returns返回的压缩后的二进制图片数据*/functionGetImgCompress(img){//如果方向角不为1,都需要进行旋转if(Orientation!=1){switch(Orientation){case6://需要顺时针90度旋转rotateImg(img,'right',canvas);break;case8://需要逆时针90度旋转rotateImg(img,'left',canvas);break;case3://需要180度旋转rotateImg(img,'right2',canvas);//转两次break;}}else{//不做旋转rotateImg(img,'no',canvas);}varndata;ndata=canvas.toDataURL(file_type,qlty);//打印压缩前后的大小,以及压缩比率//varinitSize=img.src.length;//console.log('压缩前:'+initSize);//console.log('压缩后:'+ndata.length,'base64数据',ndata);//console.log('压缩率:'+~~(100*(initSize-ndata.length)/initSize)+"%");//将压缩后的base64数据转为二进制数据ndata=dataURItoBlob(ndata);//清除canvas画布的宽高canvas.width=canvas.height=0;returnndata;}/***@actionNamerotateImg,*功能:对图片旋转处理**@paramimg,用来矫正方向的图片对象**@paramdirection,旋转方向**@paramcanvas,用来绘制图片的cavas画布对象*/functionrotateImg(img,direction,canvas){//最小与最大旋转方向,图片旋转4次后回到原方向varmin_step=0;varmax_step=3;if(img==null)return;//img的高度和宽度不能在img元素隐藏后获取,否则会出错varheight=img.height;varwidth=img.width;if(width>imgWH||height>imgWH){varratio=~~(height/width*10)/10;if(width>height){width=imgWH;height=imgWH*ratio;}else{height=imgWH;width=height/ratio;}img.width=width;img.height=height;}varstep=2;if(step==null){step=min_step;}if(direction=='no'){step=0;}elseif(direction=='left'){step++;//旋转到原位置,即超过最大值step>max_step&&(step=min_step);}elseif(direction=='right'){step--;step<min_step&&(step=max_step);}else{//旋转180度}//旋转角度以弧度值为参数vardegree=step*90*Math.PI/180;switch(step){case0:canvas.width=width;canvas.height=height;//铺底色ctx.fillStyle="#fff";ctx.fillRect(0,0,width,height);ctx.drawImage(img,0,0,width,height);break;case1:canvas.width=height;canvas.height=width;//铺底色ctx.fillStyle="#fff";ctx.fillRect(0,0,height,width);ctx.rotate(degree);ctx.drawImage(img,0,-height,width,height);break;case2:canvas.width=width;canvas.height=height;//铺底色ctx.fillStyle="#fff";ctx.fillRect(0,0,width,height);ctx.rotate(degree);ctx.drawImage(img,-width,-height,width,height);break;case3:canvas.width=height;canvas.height=width;//铺底色ctx.fillStyle="#fff";ctx.fillRect(0,0,height,width);ctx.rotate(degree);ctx.drawImage(img,-width,0,width,height);break;}}/***dataURLtoblob,refto/fupslot/5015897*@paramdataURI,图片的base64格式数据*@returns{Blob}*/functiondataURItoBlob(dataURI){varbyteString=atob(dataURI.split(',')[1]);varmimeString=dataURI.split(',')[0].split(':')[1].split(';')[0];varab=newArrayBuffer(byteString.length);varia=newUint8Array(ab);for(vari=0;i<byteString.length;i++){ia[i]=byteString.charCodeAt(i);}returnnewBlob([ab],{type:mimeString});}/***返回一个process方法**process方法:用来压缩图片数据,在压缩图片的同时,默认会调用correctOrientation方法纠正图片方向。**/returnprocess;}})(window)

【三】获取压缩后的图片二进制数据,预览图片

完成图片压缩后,就可以获取压缩后的图片二进制数据了,把获取到的图片二进制数据存起来;获取到数据后,可以拿来预览。

由于实际项目中,每个项目的UI样式设计可能不一样,开发者可以根据自己的UI样式来预览图片。

//获取压缩后的图片functiongetCompressiveFileList(fileList){blobFileList=fileList;//console.log('fileBlobList:',fileList);fileList.forEach(function(blob){varreader=newFileReader();reader.onload=function(){varli=document.createElement("LI")li.style.backgroundImage='url('+this.result+')';document.querySelector('.imgCompress_list').appendChild(li)}reader.readAsDataURL(blob);})}

【四】提交图片数据到后台

new一个formdata对象,将上一步获取到的 blobFileList 图片二进制数据 append 到 formdata中,用任意你喜欢的ajax库进行上传。当然也可以用原生ajax上传。

//将压缩后的二进制图片数据流append到formdata对象中上传到后台服务器//注意:上传的是formdata对象,后台接口接收的时候,也要从formdata对象中读取二进制数据流functionformUpData(blobFiles){varformData=newFormData();formData.append("files",blobFiles);varxhr=newXMLHttpRequest();//链接你自己上传图片接口即可,这里的接口地址,是我写的示例,不可真实使用,讲解意义更大xhr.open('post','http://xxx/welcome/index/');xhr.onreadystatechange=function(){if(xhr.readyState==4&&xhr.status==200){console.log('上传成功!');}};xhr.send(formData);}

结语

完整代码以及demo:git仓库地址

文中用到了一些H5的知识点,不懂的童鞋可以进入下面的知识点资料地址,详细阅读

HTMLCanvasElement.toDataURL()

CanvasRenderingContext2D.drawImage()

FileReader()

FormData

Blob()

EXIF.js中文博客文章

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。