用户上传图片,如果是PNG图片,有时候我们希望这张PNG图片背景不要是透明的,例如:
有时候,我们又希望上传的PNG图片带透明的,例如:
以上这些场景的处理通常这样的:
实际上,以上所有的处理都可以在上传图片的时候由前端一次性解决。
分两步:
如果是不含透明色的PNG图片,则会提示不含透明;如果是,则提示含透明,如下截图:
一句话概括就是getImageData()方法可以获取canvas画布上每一个像素点的颜色信息,于是,我们只要把上传的图片绘制在canvas上,然后检测有没有透明的像素点信息即可。
//图片绘制在画布上context.drawImage(img,0,0);//获取图片像素信息varimageData=context.getImageData(0,0,width,height).data;//检测有没有透明数据varisAlphaBackground=false;for(varindex=3;index<imageData.length;index+=4){if(imageData[index]!=255){isAlphaBackground=true;break;}}//isAlphaBackground就是最后石头有透明或半透明背景色的结果上面imageData就是我们获取的图片像素信息数组,是个一维数组,类型为Uint8ClampedArray,也就是数组中所有的值范围都是0~255,数组信息这样:
[R,G,B,A,R,G,B,A,R,G,B,A,R,G,B,A,R,G,B,A,R,G,B,A,...]因此,我们只要判断数组中的A是否全部都是255就可以判断有没有透明信息了,一个for循环就搞定了。
如果站在解决实际问题的角度,判断是否背景透明并不一定要得到。
好在,如果只是想实现一个不太复杂的图片背景色去色效果,还是挺容易的。
实现的关键依然是getImageData()方法,去色,然后把相似颜色变成透明颜色就可以了。
例如,我们点击左边图片的白色区域,然后设置容差值是20,点击“执行去色”按钮,结果如下截图:
如果我们选择紫色,则效果是:
关键2个步骤,一是取色,二是去色。
1.取色的实现
根据点击坐标,取当前点击位置像素点颜色值信息即可,代码如下:
canvas.addEventListener('click',function(event){varrect=this.getBoundingClientRect();varx=event.clientX-rect.left;vary=event.clientY-rect.top;//rgbaPicker就是点击像素点的颜色信息rgbaPicker=context.getImageData(x,y,1,1).data;});2.去色的实现
去色则是替换getImageData()的一些像素点色值为透明即可,这个不难,难的是如何判别两种色值是相似的呢?
这里有个简易的颜色色值相似度计算算法:
similar=Math.sqrt((r2-r1)*(r2-r1)+(g2-g1)*(g2-g1)+(b2-b1)*(b2-b1))其中,r2,r1表示RGB中的红色red,g2,g1表示RGB中的绿色green,b2,b1表示RGB中的蓝色blue。
OK,有了相似度算法,下面就是简单的色值替换处理了:
//像素点色值varrgba=rgbaPicker;//容差大小vartolerance=eleTolerance.value;//基于原始图片数据处理,//其中://imgData是左图像素信息,//imgDataResult是右图处理后的像素信息for(varindex=0;index<imgData.data.length;index+=4){varr=imgData.data[index];varg=imgData.data[index+1];varb=imgData.data[index+2];if(Math.sqrt((r-rgba[0])*(r-rgba[0])+(g-rgba[1])*(g-rgba[1])+(b-rgba[2])*(b-rgba[2]))<=tolerance){imgDataResult.data[index]=0;imgDataResult.data[index+1]=0;imgDataResult.data[index+2]=0;imgDataResult.data[index+3]=0;}else{imgDataResult.data[index]=r;imgDataResult.data[index+1]=g;imgDataResult.data[index+2]=b;imgDataResult.data[index+3]=imgData.data[index+3];}}//put数据contextResult.putImageData(imgDataResult,0,0);点睛代码就是最后的putImageData(),可以让画布使用新数据进行图像呈现,就可以看到去色后的效果了。
这里的去色效果是最简单的去色效果了,其实标准的去色功能,还需要一个“连续”还是“不连续”的勾选功能。
但是,对于图标和logo这些去色需求,当前的整体去色应该足够,“连续”功能有兴趣的小伙伴可以自己尝试实现下,考验连续区域算法。
忽略眼前利益的极致追求,或许是自己印刻在骨子里的特质吧,也是自己能够成长到现在这个样子的原因所在。