Canvas 10

像素操作

我们可以通过ImageData对像操纵像素数据,直接读取和写入像素数据。

ImageData对象

该对象存储着canvas对象真实的像素数据,包含下列这些只读属性:

  • width —— 图像的宽度 像素为单位
  • height —— 图像的高度 像素为单位
  • data —— 一个Unit8ClampedArray类型的一维数组,按顺序包含每个像素的位置(RGBA) 范围是0-255

我们可以通过data属性访问到原始像素数据,每个四个字节表示一个像素 对应RGBA颜色通道,每个像素由四个字节组成,每个颜色通道使用一个字节表示(0-255) 红色通道在对应像素向量中索引为0 像素数据的对应数据是从左到右然后从上至下。
Uint8ClampedArray包含宽度高度4的Unit8ClampedArray类型字节数据
我要读取50行200列的像素的蓝色通道值

const blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2];

表示获取某个像素位置的颜色值

const getColorIndicesForCoord = (x, y, width) => {
  const red = y * (width * 4) + x * 4;
  return [red, red + 1, red + 2, red + 3];
};

读取Unit8ClampedArray.length属性来获取像素数组的大小(字节为单位)

var numBytes = imageData.data.length;

创建一个ImageData对像

  • createImageData()
const myImageData = ctx.createImageData(width, height);

创建一个制定尺寸的ImageData对像,所有的像素都是透明黑色的。 你可以传入另一个ImageData对像实例创建一个相同尺寸的ImageData对像,一个新的ImageData实例。

const myImageData = ctx.createImageData(anotherImageData);

从canva画布上获取像素数据(ImageData)

  • getImageData() 获取一份关于canvas像素数据的对象
const myImageData = ctx.getImageData(left, top, width, height);

任何在画布以外的元素都会被返回成一个透明黑的 ImageData 对象。

颜色选择器

根据鼠标位置抓取对应位置在canvas上的颜色像素 示例

var img = new Image();
img.crossOrigin = "anonymous";
img.src = "./assets/rhino.jpg";
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img.onload = function () {
  ctx.drawImage(img, 0, 0);
  img.style.display = "none";
};
var hoveredColor = document.getElementById("hovered-color");
var selectedColor = document.getElementById("selected-color");

function pick(event, destination) {
  var x = event.layerX;
  var y = event.layerY;
  var pixel = ctx.getImageData(x, y, 1, 1);
  var data = pixel.data;
 const rgba = `rgba(${data[0]}, ${data[1]}, ${data[2]}, ${data[3] / 255})`;
  destination.style.background = rgba;
  destination.textContent = rgba;

  return rgba;
}

canvas.addEventListener("mousemove", function (event) {
  pick(event, hoveredColor);
});
canvas.addEventListener("click", function (event) {
  pick(event, selectedColor);
});

在canvas中写入像素数据

  • putImageData()
ctx.putImageData(myImageData, dx, dy);

dx和dy参数表示在canvas绘制像素数据起始位置。

图片的灰色和反相颜色

我们将遍历图片所有的像素修改颜色值然后使用putImageData重新放回画布中。

  • 反相颜色就是用255减去每个颜色通道值。
  • 灰度的效果设置每个颜色通道的值为的红色、绿色和蓝色的平均值。
var img = new Image();
img.crossOrigin = "anonymous";
img.src = "./assets/rhino.jpg";

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

img.onload = function () {
  ctx.drawImage(img, 0, 0);
};

var original = function () {
  ctx.drawImage(img, 0, 0);
};

var invert = function () {
  ctx.drawImage(img, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;
  for (var i = 0; i < data.length; i += 4) {
    data[i] = 255 - data[i]; // red
    data[i + 1] = 255 - data[i + 1]; // green
    data[i + 2] = 255 - data[i + 2]; // blue
  }
  ctx.putImageData(imageData, 0, 0);
};

var grayscale = function () {
  ctx.drawImage(img, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;
  for (var i = 0; i < data.length; i += 4) {
    var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
    data[i] = avg; // red
    data[i + 1] = avg; // green
    data[i + 2] = avg; // blue
  }
  ctx.putImageData(imageData, 0, 0);
};

const inputs = document.querySelectorAll("[name=color]");
for (const input of inputs) {
  input.addEventListener("change", function (evt) {
    switch (evt.target.value) {
      case "inverted":
        return invert();
      case "grayscale":
        return grayscale();
      default:
        return original();
    }
  });
}

缩放和抗锯齿

  • imageSmoothingEnabled 属性使得图像放大时显示平滑 示例,鼠标位置裁剪出距左上5像素,距离右下5像素的图片,然后复制到另一个画布调整我们想要的大小。
zoomctx.drawImage(
  canvas,
  Math.abs(x - 5),
  Math.abs(y - 5),
  10,
  10,
  0,
  0,
  200,
  200,
);

缩放示例

imageSmoothingEnabled的反锯齿效果是默认开启的。

var img = new Image();
img.src = "rhino.jpg";
img.onload = function () {
  draw(this);
};

function draw(img) {
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  img.style.display = "none";
  var zoomctx = document.getElementById("zoom").getContext("2d");

  var smoothbtn = document.getElementById("smoothbtn");
  var toggleSmoothing = function (event) {
    zoomctx.imageSmoothingEnabled = this.checked;
    zoomctx.mozImageSmoothingEnabled = this.checked;
    zoomctx.webkitImageSmoothingEnabled = this.checked;
    zoomctx.msImageSmoothingEnabled = this.checked;
  };
  smoothbtn.addEventListener("change", toggleSmoothing);

  var zoom = function (event) {
    var x = event.layerX;
    var y = event.layerY;
    zoomctx.drawImage(
      canvas,
      Math.abs(x - 5),
      Math.abs(y - 5),
      10,
      10,
      0,
      0,
      200,
      200,
    );
  };

  canvas.addEventListener("mousemove", zoom);
}

保存图片(HTMLCanvasElement)

  • toDataURL()方法,用于将canvas上像素保存成图片。(图片分辨率是 96 dpi)
  • canvas.toDataURL(‘image/png’) 默认创建一个PNG类型的图片
  • canvas.toDataURL(‘image/jpeg’, quality) 可以修改成其他类型,同时设置图片质量。
  • 生成的路径可以使用a标签保存本地或使用img标签访问展示。
  • canvas.toBlob(callback, type, encoderOptions) —— 根据canvas像素创建一个表示图片的blob对像。

写在最后

未完待续…
希望大家好好生活,健康快乐。