对于管理系统以及类似的应用来说,某个功能的本质可以理解为某一业务点。而对于专业工具以及相关的应用来说,某个功能实际上就是某个技术点。--《功能》
图片滤镜
滤镜,主要是用来实现图像的各种特殊效果。
用过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对象。
myImageData = ctx.createImageData(width, height);
上面代码创建了一个新的具体特定尺寸的ImageData对象。所有像素被预设为透明黑。
获取场景像素数据
我们可以用getImageData()
方法获取画布场景中的像素数据
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);
});
- 在canvas中绘制图片
- 移动鼠标时获取鼠标的位置,该位置就是此时鼠标所在的像素点的位置
- 点击鼠标时,获取该像素点的颜色信息。
在场景中写入像素数据
我们可以使用putImageData()
方法修改像素数据后,对画布场景数据进行重置。
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。