当前位置: 首页 > Web前端 > HTML5

JavaScript中的图像处理与合成(一)

时间:2023-04-05 21:30:05 HTML5

JavaScript中的图像处理与合成(一)简介:图像处理现在已经成为我们生活中的刚需,想必每个人都经常有这种需求。在实际的前端业务中,往往有很多项目需要进行图像处理和处理。过去一段时间因公司业务需要,积累了一些这方面的干货,会在年后的时间里总结成系列文章分享给大家,希望能给大家带来启发和帮助致前端童鞋们辛苦了~~~?本系列分为以下四个部分:图像处理基础技术的缩放与裁剪;图像合成基本图像处理技术;基本图像处理技术的文本合成;算法图像处理技术;看到很多坑或者是在实战中遇到的经验,应该算是干货满满了~~如果能够通读的话,应该能够大大提升你对前端图像处理领域的理解。有兴趣的童鞋可以和我深入探讨。希望这篇文章能够达到抛砖引玉的效果,让前端在图像处理上有更多的可能。不足之处还请见谅。通过这些积累,我封装了几个项目中常用的功能:图片合成:ExampleGit图片裁剪:ExampleGit人像抠图:ExampleGit唠叨完这些老套路,我们就开始起飞了!~~??????首先我把前端图像处理暂且分为两种:基本型和算法型;基本类型的图像处理技术:图像缩放、旋转、加边框、图像合成、拼图等服务都是基本类型图像处理的区别在于不需要使用像素级算法,而是通过计算对图像进行变换并改变图像的大小和位置。比如常用的贴纸函数:算法型图像处理:这类图像处理比较复杂,其特点是通过像素级算法将图像的像素点通过RGBA通道值进行变换。比如我们用photshop或者美图秀秀工具对图片进行美化/滤镜/黑白/抠图/模糊等操作,这一类的重点主要在算法和性能层面。比如常用的彩妆功能:本系列从基本的妆容类型开始我们的旅程。图像处理的基本类型在实际项目中有大量的使用场景。主要是利用canvas的能力完成的。不存在性能和兼容性问题,可以满足在线运行标准。这里我将图像处理的基本类型大致分为以下几种,基本可以涵盖所有日常业务场景:图像缩放;裁剪图像;图像合成;图像和图像的合成,例如贴纸、边框、水印等;给图片添加文字;为图片添加基本几何图形;Tips:我已经将此类图片处理场景封装成一个插件,基本可以满足此类图片处理的所有需求,git地址(欢迎讨论);在介绍具体功能之前,由于图片的绘制完全依赖于图片的加载,我们先了解一些前置知识。1.跨域图片首先加载和绘制图片涉及到图片的跨域问题,所以如果是在线图片,需要在图片服务器上设置一个跨域的header,并在之前设置标签前端加载图片crossOrigin设置为*,否则绘制到canvas时会报跨域错误。Tips:这里积累了一些坑,分享给大家:1.crossOrigin需要严格设置,即只有在线图片的时候才可以设置,在线图片的时候一定不能设置本地路径或base64,否则在某些系统中2.当项目是本地包环境时,比如构建成App时,crossOrigin值无效,webview的安全机制会导致跨域错误无论是否设置该值都会被报告。解决方法是:所有图片都需要转成base64才能正确绘制;3、crossOrigin值必须在图片加载前设置,即在将src赋值给之前设置,否则无效;2.图片的加载是由于canvas的绘制需要的是已经加载好的图片。我们需要保证绘制的素材图片已经加载,所以需要使用的onload事件,可以使用html中已有的图片,也可以用js创建图片对象:functionloadImage(image,loader,error){//创建一个图像对象来加载图像;让img=newImage();//当是在线图片时,需要设置crossOrigin属性;if(image.indexOf('http')==0)img.crossOrigin='*';img.onload=()=>{加载(img);//使用后清空对象,释放内存;setTimeout(()=>{img=null;},1000);};img.onerror=()=>{error('img加载错误');};img.src=image;}介绍完图片加载的前置知识,我们先来看最简单的图片处理---缩放裁剪!Tips:相信看到这篇文章的你,如果对canvas还不是很了解,可以去查阅相应的API文档。本文不会详细讲解canvas的基本API。1.图像缩放图像缩放最常见的场景是图像压缩。在保证图片清晰的前提下,通过合理缩小图片尺寸,可以大大缩小图片尺寸。在实际应用场景中,它有着广泛的用途。例如,在上传图片时,用户上传的图片可能尺寸非常大。例如,手机拍摄的照片尺寸往往可以达到1920*2560,尺寸可能超过5M。在项目中,我们可能不需要使用这么大的尺寸。这时候图片压缩可以大大优化加载速度,节省带宽;1.新建画布,设置宽高为需要压缩的尺寸;画布为图片缩放后的尺寸。这里有一点需要保持图片的比例不变,所以需要计算canvas的宽高:letimgRatio=img.naturalWidth/img.naturalHeight;//创建一个canvas容器;letcvs=document.createElement('canvas');//获取容器中的画板;让ctx=cvs.getContext('2d');cvs.width=1000;cvs.height=cvs.width/imgRatio;2.绘制图像,然后导出为base64;这里介绍两个最常用的方法:ctx.drawImage(image,dx,dy,dw,dh):这个方法实际上最多可以接收9个参数,实现压缩只需要用到其中的5个参数,以及其余参数将在其他部分使用时详细说明;image:要绘制的图片源,需要接收加载的HTMLImageElement、HTMLCanvasElement或HTMLVideoElement;dx/dy:相对于画布左上角绘图起点的坐标;dw/dh:绘图的宽高,不锁定纵横比,图像可以变形;cvs.toDataURL(type,quality):该方法用于将画布上的内容以base64格式导出图片,可以配置2个参数;type:图片格式,一般可以使用image/png或image/jpeg,当图片不包含透明度时,推荐使用jpeg,可以大大减小导出图片的尺寸;quality:图像质量,可以使用0到1之间的任意值;经过测试,将该值设置为0.9比较合适,可以有效减小图片文件的大小,基本不影响图片的清晰度。导出的base64压缩后的最终图片;Tips:这里有个坑,如果要导出jpg格式的图片,必须用image/jpeg,不能用image/jpg;//在缩放后的画布上等比例绘制原始图像;ctx.drawImage(image,0,0,cvs.width,cvs.height);//将绘制的图片导出为base64格式;让b64=cvs.toDataURL('image/jpeg',0.9);3.将多种格式的图片转成base64;我们使用原生图片上传功能标签的标签。此时获取的是File格式的图片。图像格式不同,尺寸较大。我们应该在使用它之前压缩它。使用FileReader:letfile=e.files[0];if(window.FileReader){让fr=newFileReader();fr.onloadend=e=>{让b64=e.target.result;//b64为用户上传的base64格式的图片;};神父readAsDataURL(file);}使用刚才的canvas方法对base64图片进行压缩;Tips:这里有一个小坑,图片的EXIF信息中的方向值会影响图片的显示,在IOS上会出现图片的宽高与图片的方向不符,所以需要特殊的处理来修正图片的方向。解决方法:1、可以使用exif.js获取图片信息中的Orientation属性,使用canvas的旋转绘制进行修正;2、这里有个canvasResize.js插件,可以解决从File到base64的所有问题。2.图片裁剪在实际项目中,由于图片的纵横比多种多样,显示和使用一般都需要一个相对固定的比例。这时候就需要将图片裁剪成我们需要的宽高比。方法其实和图片的缩放基本一样,主要是调整drawImage的dx和dy参数。原理其实就是将drawImage的绘制起点(dx,dy)往上偏移。这时,由于canvas已经被我们设置为预期的裁剪尺寸,超出canvas的部分将不会被绘制,从而达到裁剪的目的;通过灵活的设置值,基本可以满足各种图像裁剪需求。简单示例图片(黑框代表创建的画布大小):这里需要对一张600*800的矩形图片进行垂直居中裁剪成600*600的正方形以图片为例,简单封装成一个函数function//使用方法:letb64=cropImage(img,{width:600,height:600,});//centercroppingfunctioncropImage(img,ops){//图像原始尺寸;让imgOriginWidth=img.naturalWidth,imgOriginHeight=img.naturalHeight;//图片纵横比,保证图片不变形;让imgRatio=imgOriginWidth/imgOriginHeight;//裁剪后图片的宽高,默认值为原图宽高;让imgCropedWidth=ops.width||imgOriginWidth,imgCropedHeight=ops.height||img原点高度;//计算起始坐标点的偏移量,因为是居中裁剪,所以等于前后差值/2;让dx=(imgCropedWidth-imgOriginWidth)/2,dy=(imgCropedHeight-imgOriginHeight)/2;//创建画布并将画布设置为裁剪后的宽高;让cvs=document.createElement('canvas');让ctx=cvs.getContext('2d');cvs.width=imgCropedWidth;cvs.height=imgCropedHeight;//绘制和导出图像;ctx.drawImage(img,dx,dy,imgCropedWidth,imgCropedWidth/imgRatio);returncvs.toDataURL('image/jpeg',0.9);}3.图片旋转图片旋转的原理也是在画布上绘制图片导出前旋转其实就是用到了canvas的rotate方法;让cvs=document.createElement('canvas');letctx=cvs.getContext('2d');//将参考点移动到绘图板的中心点;ctx.translate(ctx.width/2,ctx.height/2);//旋转画板;ctx.rotate=90;//绘制图像;ctx.drawImage(img);//导出旋转后的图像;cvs.toDataURL();这里有个特殊的地方,就是这里旋转的是canvas的画板部分,不是整个canvas容器,canvas容器外面不会画出来,所以会有一个图片四个角会被裁剪的问题:解决方法:将canvas容器放大为:上例中,由于图片是正方形,将容器的宽高放大1.5倍可以保证图片不会被裁剪,但真实图片由于宽高比不确定,所以这个放大倍数是一个动态值:Tips:由于我们把画板的基点移到了画布的中心,所以在绘制的时候,我们需要相对于基点调整dx和dy;//创建画布并获取画板;...//放大倍数令iw=img.width,ih=img.height;让ir=iw>ih?iw/ih:ih/iw;cvs.width=iw*ir*1.5;cvs.height=ih*ir*1.5;//将参考点移动到画板的中心;ctx.translate(cvs.width/2,cvs.height/2);//旋转画板;ctx.rotate=90;//画图;ctx.drawImage(img,-cvs.width/2,-cvs.height/2);//导出图片;...总结本文主要介绍前端图像处理的一些前置知识:图像处理技术分类;基本类型图像处理技术;算法型图像处理技术;图片跨域;图片加载;也解释了属于基本类型的两种最简单的图像处理:图像缩放;图像裁剪;图像旋转;相信大家已经明白我对图像处理有了一个大概的了解。下一篇继续研究基础类型中的图像合成,也是干货满满,好看~~???最后,非常感谢大家的阅读,有什么建议或者疑问可以随时和我讨论~~