使用 canvas 处理图像

337 阅读3分钟

canvas 可以直接将图像渲染到画布上,并对图像进行旋转、变形、滤镜处理。

一、导入图片

首先我们需要导入一张图片,有几种方式:

本地方式:

通过下面两种方式获取 file

  • 设置一个拖放区域,将本地的图片拖放进去:drop 事件中获取 event.dataTrnsfer.files
  • 通过 <input type="file" /> 标签,change 事件获取 event.target.files

然后,实例一个 FileReader,用 readAsDataURL(file) 来读取文件。当读取完成,触发 load 事件,可获取到 base64 的编码:

const reader = new FileReader()
reader.addEventListener('load', event => {
    console.log(event.target.result)  // data:URL 格式的字符串(base64 编码)
})
reader.readAsDataURL(file)

远程数据:

上传,或者直接选择一个网络图片,获取其 url

二、将图片绘制到画布上

首先需要 new 一个 Image 对象 img,但它不需要插入到 dom 中。设置 src 属性和 load 事件。

然后通过 canvascontextdrawImage 方法来将 img 对象渲染到画布上。注意这个方法有多种参数形式,是可以对图片渲染进行缩放、裁切和定位。

const img = new Image()
img.addEventListener('load', () => {
    ctx.drawImage(img)
})
img.src = result // 这个 result 可以是 data:URL 格式字符串,或者 url

三、像素处理

获取 ImageData

要进行像素处理,需要先了解 ImageData 对象。它是描述图像像素信息的对象。我们可以通过 getImageDatactx 上获取:

const myImageData = ctx.getImageData(left, top, width, height)

它有三个属性:

  • width:横向的像素
  • height:纵向的像素
  • dataUint8ClampedArray 这样的数据结构,和数组非常类似。它记录着所有的像素信息:
    • 每四个成员表示一个像素,分别代表它的 r,g,b,a
    • 每个成员取值是 [0,255],赋值如果超出则会被替代为 0 或 255,所以没有必要使用 Math.min(value, 255) 这样的方式去限定它

处理 ImageData

滤镜,其实就是对每个像素的 r,g,b,a 进行重新赋值。

灰度

一般有几种算法:

  • 取 rgb 的平均值
  • 取 rgb 的最大值
  • 使用加权公式:0.3 * r + 0.59 * g + 0.11 * b

比如采用平均值算法:

const { data } = imageData

// 先需要把元数据备份一份
const dataSource = [ ...data ]

// 直接修改 imageData.data 的值
function changeGray(percent) {
    for (let i = 0; i < dataSource.length; i += 4) {
        const avg = (dataSource[i] + dataSource[i + 1] + dataSource[i + 2]) / 3;
        data[i] = Math.min(dataSource[i] + (avg - dataSource[i]) * percent, 255); // red
        data[i + 1] = Math.min(dataSource[i + 1] + (avg - dataSource[i + 1]) * percent, 255); // green
        data[i + 2] = Math.min(dataSource[i + 2] + (avg - dataSource[i + 2]) * percent, 255); // blue
    }
}

黑白

黑白就是 rgb 的平均值,大于 255 / 2 就给这三位赋值 255,否则就是 0

反色

反色就是 rgb 分别赋值 255 - value

模糊

每个像素点和周围像素点互相影响

让改动生效

使用 ctx.putImageData(imageData, 0, 0) 让改动后的 imageData 在画布上生效

四、生成 base64 及下载

直接调用 canvas.toDataURL() 方法,可以将画布转化为 base64 的编码的字符串。这个方法可以接受这两参数:

  • type(可选):默认为 image/png,也可以传入 image/jpegimage/webp(chrome 专属)
  • encoderOptions(可选): jpeg 和 webp 的格式可以设置图片质量,取值 0 ~ 1 之间

生成的 base64 我们可以用来下载,除了用户自己右键下载,或者我们通过服务器来返回下载外,我们还可以生成个 a 标签,直接下载:

downloadImage(name) {
    const a = document.createElement('a');
    a.href = canvas.toDataURL();
    a.download = name ? name : '下载'
    a.click()
}