Toninie / toninie.github.io

https://Toninie.github.io
1 stars 0 forks source link

使用canvas对图片数据处理的一些总结 #3

Open Toninie opened 6 years ago

Toninie commented 6 years ago

这个是去年使用canvas对图片尺寸进行压缩、裁剪时总结的一些经验,重新整理了内容搬来这里。

本文提及的现象可以通过此demo感受一下,另外安利一下后续整理集成的海报编辑插件

canvas显示图片

使用canvas来显示图片时,一般用到drawImage这个方法,调用方式有3种。

  1. 定位画图:
    context.drawImage(img,x,y);
  2. 在画布上定位图像,并规定图像的宽度和高度:
    context.drawImage(img,x,y,width,height);
  3. 剪切图像,并在画布上定位被剪切的部分:
    context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);  

    其中参数的含义:

    • img 规定要使用的图像、画布或视频。
    • sx 可选,开始剪切的 x 坐标位置。
    • sy 可选,开始剪切的 y 坐标位置。
    • swidth 可选,被剪切图像的宽度。
    • sheight 可选,被剪切图像的高度。
    • x 在画布上放置图像的 x 坐标位置。
    • y 在画布上放置图像的 y 坐标位置。
    • width 可选,要使用的图像的宽度。
    • height 可选。要使用的图像的高度。

widthheight与图片实际尺寸不同时,即会自动被压缩或拉伸。

现象和问题

使用canvas显示图片时,常见的情况是图片实际宽度与canvas不匹配,需要对图片尺寸进行压缩,如果直接用canvas的方法去强制压缩以适应尺寸,例如:

var c = $("#myCanvas")[0],      
    ctx = c.getContext("2d"),
    img = new Image(),
    maxWidth = $(window).width();

img.src = ".../image/xxx.jpg";

img.onload = function () {
    c.width = maxWidth;
    c.height = maxWidth * this.height / this.width;
    ctx.drawImage(img,0,0,c.width,c.height);
}

显示出来的图片很容易出现模糊或者锯齿,如果原图尺寸与canvas尺寸差距较大时,这种现象会更明显。

解决方法

显示优化

可以将图片按原尺寸呈现,再通过css去调整canvas的尺寸,这种方法是不会对加载到canvas的图片数据产生影响。

img.onload = function () {
    c.width = this.width;// 将canvas宽度设置为原图宽度
    c.height = this.height;// 将canvas高度设置为原图高度
    ctx.drawImage(img,0,0,c.width,c.height);
    c.style.width = "100%";// 修改canvas的样式,适应页面
}

压缩优化

之前已经提到,原图尺寸与canvas尺寸差距较大时,直接通过canvas压缩图片会出现锯齿、模糊等现象,质量很难得到保证,但是要在前端对图片数据进行修改,还是离不开canvas。 要实现高质量的图片压缩,首先还是按原尺寸将图片加载到canvas上,然后获取数据

context.getImageData(int x,int y,int width,int height)

canvas上从(x,y)开始,获取宽为width、高为height的图片数据。该方法返回的是一个CanvasPixelArray对象。 该对象具有widthheightdata等属性,其中data属性可以看作一个数组来操作。该数组每4个元素对应一个像素点,依次记录像素点的rgba数值,像素点的顺序是从左到右,从上至下。 可以通过算法将图片的像素点从原尺寸压缩成目标尺寸,再重新加载到canvas

context.putImageData(CanvasPixelArray data,int x,int y)

能够自定义算法去操作图片每个像素点的rgba数据,这里的自由度就非常高了。这里安利一个现成的js插件scale.js,能够比较高质量地压缩图片,当然使用的算法也比较简直接,就是通过面积加权的算法,来得出压缩后每个像素点的颜色值。