Canvas API:图像处理

CanvasRenderingContext2D.drawImage()

Canvas API 允许将图像文件写入画布,做法是读取图片后,使用drawImage()方法将这张图片放上画布。

CanvasRenderingContext2D.drawImage()有三种使用格式。

  1. ctx.drawImage(image, dx, dy);
  2. ctx.drawImage(image, dx, dy, dWidth, dHeight);
  3. ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

各个参数的含义如下。

  • image:图像元素
  • sx:图像内部的横坐标,用于映射到画布的放置点上。
  • sy:图像内部的纵坐标,用于映射到画布的放置点上。
  • sWidth:图像在画布上的宽度,会产生缩放效果。如果未指定,则图像不会缩放,按照实际大小占据画布的宽度。
  • sHeight:图像在画布上的高度,会产生缩放效果。如果未指定,则图像不会缩放,按照实际大小占据画布的高度。
  • dx:画布内部的横坐标,用于放置图像的左上角
  • dy:画布内部的纵坐标,用于放置图像的右上角
  • dWidth:图像在画布内部的宽度,会产生缩放效果。
  • dHeight:图像在画布内部的高度,会产生缩放效果。

下面是最简单的使用场景,将图像放在画布上,两者左上角对齐。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. var img = new Image();
  4. img.src = 'image.png';
  5. img.onload = function () {
  6. ctx.drawImage(img, 0, 0);
  7. };

上面代码将一个 PNG 图像放入画布。这时,图像将是原始大小,如果画布小于图像,就会只显示出图像左上角,正好等于画布大小的那一块。

如果要显示完整的图片,可以用图像的宽和高,设置成画布的宽和高。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. var image = new Image(60, 45);
  4. image.onload = drawImageActualSize;
  5. image.src = 'https://example.com/image.jpg';
  6. function drawImageActualSize() {
  7. canvas.width = this.naturalWidth;
  8. canvas.height = this.naturalHeight;
  9. ctx.drawImage(this, 0, 0, this.naturalWidth, this.naturalHeight);
  10. }

上面代码中,<canvas>元素的大小设置成图像的本来大小,就能保证完整展示图像。由于图像的本来大小,只有图像加载成功以后才能拿到,因此调整画布的大小,必须放在image.onload这个监听函数里面。

像素读写

以下三个方法与像素读写相关。

  • CanvasRenderingContext2D.getImageData():将画布读取成一个 ImageData 对象
  • CanvasRenderingContext2D.putImageData():将 ImageData 对象写入画布
  • CanvasRenderingContext2D.createImageData():生成 ImageData 对象

(1)getImageData()

CanvasRenderingContext2D.getImageData()方法用来读取<canvas>的内容,返回一个 ImageData 对象,包含了每个像素的信息。

  1. ctx.getImageData(sx, sy, sw, sh)

getImageData()方法接受四个参数。sxsy是读取区域的左上角坐标,swsh是读取区域的宽度和高度。如果想要读取整个<canvas>区域,可以写成下面这样。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

getImageData()方法返回的是一个ImageData对象。该对象有三个属性。

  • ImageData.data:一个一维数组。该数组的值,依次是每个像素的红、绿、蓝、alpha 通道值(每个值的范围是 0~255),因此该数组的长度等于图像的像素宽度 x 图像的像素高度 x 4。这个数组不仅可读,而且可写,因此通过操作这个数组,就可以达到操作图像的目的。
  • ImageData.width:浮点数,表示 ImageData 的像素宽度。
  • ImageData.height:浮点数,表示 ImageData 的像素高度。

(2)putImageData()

CanvasRenderingContext2D.putImageData()方法将ImageData对象的像素绘制在<canvas>画布上。该方法有两种使用格式。

  1. ctx.putImageData(imagedata, dx, dy)
  2. ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)

该方法有如下参数。

  • imagedata:包含像素信息的 ImageData 对象。
  • dx:<canvas>元素内部的横坐标,用于放置 ImageData 图像的左上角。
  • dy:<canvas>元素内部的纵坐标,用于放置 ImageData 图像的左上角。
  • dirtyX:ImageData 图像内部的横坐标,用于作为放置到<canvas>的矩形区域的左上角的横坐标,默认为0。
  • dirtyY:ImageData 图像内部的纵坐标,用于作为放置到<canvas>的矩形区域的左上角的纵坐标,默认为0。
  • dirtyWidth:放置到<canvas>的矩形区域的宽度,默认为 ImageData 图像的宽度。
  • dirtyHeight:放置到<canvas>的矩形区域的高度,默认为 ImageData 图像的高度。

下面是将 ImageData 对象绘制到<canvas>的例子。

  1. ctx.putImageData(imageData, 0, 0);

(3)createImageData()

CanvasRenderingContext2D.createImageData()方法用于生成一个空的ImageData对象,所有像素都是透明的黑色(即每个值都是0)。该方法有两种使用格式。

  1. ctx.createImageData(width, height)
  2. ctx.createImageData(imagedata)

createImageData()方法的参数如下。

  • width:ImageData 对象的宽度,单位为像素。
  • height:ImageData 对象的高度,单位为像素。
  • imagedata:一个现有的 ImageData 对象,返回值将是这个对象的拷贝。
  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. var imageData = ctx.createImageData(100, 100);

上面代码中,imageData是一个 100 x 100 的像素区域,其中每个像素都是透明的黑色。

CanvasRenderingContext2D.save(),CanvasRenderingContext2D.restore()

CanvasRenderingContext2D.save()方法用于将画布的当前样式保存到堆栈,相当于在内存之中产生一个样式快照。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.save();

上面代码中,save()会为画布的默认样式产生一个快照。

CanvasRenderingContext2D.restore()方法将画布的样式恢复到上一个保存的快照,如果没有已保存的快照,则不产生任何效果。

上下文环境,restore方法用于恢复到上一次保存的上下文环境。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.save();
  4. ctx.fillStyle = 'green';
  5. ctx.restore();
  6. ctx.fillRect(10, 10, 100, 100);

上面代码画一个矩形。矩形的填充色本来设为绿色,但是restore()方法撤销了这个设置,将样式恢复上一次保存的状态(即默认样式),所以实际的填充色是黑色(默认颜色)。

CanvasRenderingContext2D.canvas

CanvasRenderingContext2D.canvas属性指向当前CanvasRenderingContext2D对象所在的<canvas>元素。该属性只读。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.canvas === canvas // true

图像变换

以下方法用于图像变换。

  • CanvasRenderingContext2D.rotate():图像旋转
  • CanvasRenderingContext2D.scale():图像缩放
  • CanvasRenderingContext2D.translate():图像平移
  • CanvasRenderingContext2D.transform():通过一个变换矩阵完成图像变换
  • CanvasRenderingContext2D.setTransform():取消前面的图像变换

(1)rotate()

CanvasRenderingContext2D.rotate()方法用于图像旋转。它接受一个弧度值作为参数,表示顺时针旋转的度数。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.rotate(45 * Math.PI / 180);
  4. ctx.fillRect(70, 0, 100, 30);

上面代码会显示一个顺时针倾斜45度的矩形。注意,rotate()方法必须在fillRect()方法之前调用,否则是不起作用的。

旋转中心点始终是画布左上角的原点。如果要更改中心点,需要使用translate()方法移动画布。

(2)scale()

CanvasRenderingContext2D.scale()方法用于缩放图像。它接受两个参数,分别是x轴方向的缩放因子和y轴方向的缩放因子。默认情况下,一个单位就是一个像素,缩放因子可以缩放单位,比如缩放因子0.5表示将大小缩小为原来的50%,缩放因子10表示放大十倍。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.scale(10, 3);
  4. ctx.fillRect(10, 10, 10, 10);

上面代码中,原来的矩形是 10 x 10,缩放后展示出来是 100 x 30。

如果缩放因子为1,就表示图像没有任何缩放。如果为-1,则表示方向翻转。ctx.scale(-1, 1)为水平翻转,ctx.scale(1, -1)表示垂直翻转。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.scale(1, -2);
  4. ctx.font = "16px serif";
  5. ctx.fillText('Hello world!', 20, -20);

上面代码会显示一个水平倒转的、高度放大2倍的Hello World!

注意,负向缩放本质是坐标翻转,所针对的坐标轴就是画布左上角原点的坐标轴。

(3)translate()

CanvasRenderingContext2D.translate()方法用于平移图像。它接受两个参数,分别是 x 轴和 y 轴移动的距离(单位像素)。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.translate(50, 50);
  4. ctx.fillRect(0, 0, 100, 100);

(4)transform()

CanvasRenderingContext2D.transform()方法接受一个变换矩阵的六个元素作为参数,完成缩放、旋转、移动和倾斜等变形。

它的使用格式如下。

  1. ctx.transform(a, b, c, d, e, f);
  2. /*
  3. a:水平缩放(默认值1,单位倍数)
  4. b:水平倾斜(默认值0,单位弧度)
  5. c:垂直倾斜(默认值0,单位弧度)
  6. d:垂直缩放(默认值1,单位倍数)
  7. e:水平位移(默认值0,单位像素)
  8. f:垂直位移(默认值0,单位像素)
  9. */

下面是一个例子。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.transform(2, 0, 0, 1, 50, 50);
  4. ctx.fillRect(0, 0, 100, 100);

上面代码中,原始图形是 100 x 100 的矩形,结果缩放成 200 x 100 的矩形,并且左上角从(0, 0)移动到(50, 50)

注意,多个transform()方法具有叠加效果。

(5)setTransform()

CanvasRenderingContext2D.setTransform()方法取消前面的图形变换,将画布恢复到该方法指定的状态。该方法的参数与transform()方法完全一致。

  1. var canvas = document.getElementById('myCanvas');
  2. var ctx = canvas.getContext('2d');
  3. ctx.translate(50, 50);
  4. ctx.fillRect(0, 0, 100, 100);
  5. ctx.setTransform(1, 0, 0, 1, 0, 0);
  6. ctx.fillRect(0, 0, 100, 100);

上面代码中,第一个fillRect()方法绘制的矩形,左上角从(0, 0)平移到(50, 50)setTransform()方法取消了这个变换(已绘制的图形不受影响),将画布恢复到默认状态(变换矩形1, 0, 0, 1, 0, 0),所以第二个矩形的左上角回到(0, 0)