前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Canvas如何实现滤镜效果

Canvas如何实现滤镜效果

作者头像
terrence386
发布2022-07-15 09:27:18
1.2K0
发布2022-07-15 09:27:18
举报

对于管理系统以及类似的应用来说,某个功能的本质可以理解为某一业务点。而对于专业工具以及相关的应用来说,某个功能实际上就是某个技术点。--《功能》

图片滤镜

滤镜,主要是用来实现图像的各种特殊效果。

用过photoshop或者美颜相机,我们都知道滤镜可以帮助我们把图片修缮的更加完美。

那么,作为前端开发人员,如何实现一套滤镜效果呢?一起来了解下吧。

实现滤镜需要了解的内容

我们知道前端通过Canvas可以绘制各种图形,文本,图片,只需要通过fillRect,fillText,drawImage等相关的Api即可绘制对应的内容。

但实际,我们可以通过直接操作ImageData对象来修改像素数据,从而实现各种我们想要的效果。

ImageData 对象

ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:

  • width: 图片宽度,单位px
  • height: 图片高度,单位px
  • data: Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255)。

data属性返回一个 Uint8ClampedArray,它可以被使用作为查看初始像素数据。每个像素用4个1bytes值(按照红,绿,蓝和透明值的顺序; 这就是"RGBA"格式) 来代表。每个颜色值部份用0至255来代表。每个部份被分配到一个在数组内连续的索引,左上角像素的红色部份在数组的索引0位置。像素从左到右被处理,然后往下,遍历整个数组。

Uint8ClampedArray 包含高度 × 宽度 × 4 个比特数据,索引值从0到(高度×宽度×4)-1

假如要读取图片中位于第50行,第200列的像素的蓝色部份,则可写以下代码:

代码语言:javascript
复制
blueComponent = imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 2];

根据行、列读取某像素点的R/G/B/A值的公式:

代码语言:javascript
复制
imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 0/1/2/3];

也可以使用Uint8ClampedArray.length属性来读取像素数组的大小(以bytes为单位):

代码语言:javascript
复制
let numBytes = imageData.data.length;

创建ImageData对象

我们可以使用createImageData()方法创建ImageData对象。

代码语言:javascript
复制
 myImageData = ctx.createImageData(width, height);

上面代码创建了一个新的具体特定尺寸的ImageData对象。所有像素被预设为透明黑。

获取场景像素数据

我们可以用getImageData()方法获取画布场景中的像素数据

代码语言:javascript
复制
let myImageData = ctx.getImageData(left, top, width, height);

这个方法会返回一个ImageData对象,它代表了画布区域的对象数据,此画布的四个角落分别表示为(left, top), (left + width, top), (left, top + height), 以及(left + width, top + height)四个点。这些坐标点被设定为画布坐标空间元素。

Canvas实现拾色器

前端在实现设计稿时,通常会使用拾色器,也叫做吸色工具。基于canvas下面的代码是一种简单的实现。

代码语言:javascript
复制
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);
});
  1. 在canvas中绘制图片
  2. 移动鼠标时获取鼠标的位置,该位置就是此时鼠标所在的像素点的位置
  3. 点击鼠标时,获取该像素点的颜色信息。

在场景中写入像素数据

我们可以使用putImageData()方法修改像素数据后,对画布场景数据进行重置。

代码语言:javascript
复制
ctx.putImageData(myImageData, dx, dy);

图片灰度和反相颜色

下面的例子,我们遍历所有像素以改变他们的数值。然后将被修改的像素数组通过putImageData()放回到画布中去。invert函数仅仅是去减掉颜色的最大色值255.grayscale函数仅仅是用红绿和蓝的平均值。我们也可以用加权平均,例如x = 0.299r + 0.587g + 0.114b这个公式。

代码语言:javascript
复制
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);
};
// radio变化
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();
        }
    });
}

原图

灰度

类似的,我们可以实现各种滤镜效果,比如:浮雕,高斯模糊,镜像,复古...

浮雕

最后

前端实现滤镜的方式其实有两种,一种是canvas,另一种是css。

从canvas来讲,可以通过修改canvas中的图片像素数据来实现各种滤镜效果,但是需要我们自己去修改像素值,自己去查各种算法。

而css相反,我们可以直接使用css的filter来设置各种效果,因为它已经内置了很多滤镜效果。

总结

像素操作需要注意的是,像素点是每四个一组,分别代表:R,G,B,A。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 图片滤镜
  • 实现滤镜需要了解的内容
    • ImageData 对象
      • 创建ImageData对象
        • 获取场景像素数据
          • Canvas实现拾色器
            • 在场景中写入像素数据
              • 图片灰度和反相颜色
              • 最后
                • 总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档