昨天各大平台收到通知,需要首页以黑白效果来展示,高级浏览器有一个简单的属性,全局设置一下,就可以满足:
-webkit-filter: grayscale(100%);filter: grayscale(100%);
在低级浏览器可以借助第三方插件来实现,比如说grayscale.js这种。
这种插件的原理,简单来说就是递归DOM结构,判断节点的类型,如果是文本直接更改其颜色值,如果是图片,通过canvas来更改颜色值。
如何改变图片的颜色呢,就是今天咱们来聊的色彩矩阵。
其实就是把每个像素值跟多维矩阵乘一下就得到一个新的像素值(rgba)(底部有封装好的初始矩阵通道)
matrix!:Array = [];
RGBA = [ R, G, B, A ]
matrix =[ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ] //原始通道
新的颜色信息 = RGBA * matrix = [
matrix[0]*R + matrix[1]*G + matrix[2]*B + matrix[3]*A + matrix[4] // R通道
matrix[5]*R + matrix[6]*G + matrix[7]*B + matrix[8]*A + matrix[9] // G通道
matrix[10]*R + matrix[11]*G + matrix[12]*B + matrix[13]*A + matrix[14] // B通道
matrix[15]*R + matrix[16]*G + matrix[17]*B + matrix[18]*A + matrix[19] // A通道
]
matrix由 20 个项目组成的数组,适用于 4 x 5 颜色转换。
red通道的值:(1,0,0,0,0)表示,R通道的乘数是1(完全保留),别的道道的的乘数是0,(不加入别的通道的颜色),色彩偏移量off是0;
green通道的值:(0, 1, 0, 0, 0)表示,G通道的乘数是1(完全保留),别的道道的的乘数是0,(不加入别的通道的颜色),色彩偏移量off是0;
blue通道的值:(0, 0, 1, 0, 0)表示,B通道的乘数是1(完全保留),别的道道的的乘数是0,(不加入别的通道的颜色),色彩偏移量off是0;
alpha通道的值:(0, 0, 0, 1, 0)表示,A通道的乘数是1(完全保留),别的道道的的乘数是0,(不加入别的通道的颜色),色彩偏移量off是0;
4 x 5 4行5列我们已经知晓其4列(RGBA),至于第5列(简称N)是颜色亮度的调节:
1,0,0,0,N
0,1,0,0,N
0,0,1,0,N
0,0,0,1,0
N=>亮度取值范围-255到255,只需要设置一下RGB的色彩偏移就能调节其亮度
举例说明:如果想要一个值为 rgba(200, 100, 50, 1 ) 的颜色,只保留红色通道的色彩。我们只需要调整 matrix 并做如下运算:
[ 200, 100, 50, 1 ] * [ 1, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0 ] = [
1*200 + 0*100 + 0*50 + 0*1 + 0,
0*200 + 0*100 + 0*50 + 0*1 +0,
0*200 + 0*100 + 0*50 + 0*1 +0,
0*200 + 0*100 + 0*50 + 1*1 +0,
] = [ 200, 0, 0, 1 ]
颜色反转
[ 200, 100, 50, 1 ] * [ -1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0 ] = [
-1*200 + 0*100 + 0*50 + 0*1 + 255,
0*200 + -1*100 + 0*50 + 0*1 +255
0*200 + 0*100 + -1*50 + 0*1 +255,
0*200 + 0*100 + 0*50 + 1*1 +0,
] = [ 206, 206, 206, 1]
咱们昨天的黑白是如何实现的呢,颜色去色,同样是矩阵算法,但是通道值却不是跟上面一样的1:
matrix = [ 0.3086, 0.6094, 0.0820, 0, 0
0.3086, 0.6094, 0.0820, 0, 0
0.3086, 0.6094, 0.0820, 0, 0
0 , 0 , 0, 1, 0 ]
只要把RGB三通道的色彩信息设置成一样;即:R=G=B,那么图像就变成了灰色,并且,为了保证图像亮度不变,同一个通道中的R+G+B=1,很多人会奇怪为啥是这三个小数,其实是把RGB做了一个比例均分:大概是3:6:1,即:0.3086, 0.6094, 0.0820
看到这里来了,应该就会明白更改图片颜色的关键是矩阵乘法,通过不同的多维矩阵原始值,来达到不同的效果,包括咱们看到的微信图片滤镜,以及社交平台ins的图片滤镜,也是通过多维矩阵算法来实现一个图片滤镜。
下面看看,我们可以利用这个玩意最终来做成什么效果(家中浩克出来当次模特吧):
以下是每个不同效果封装好的初始矩阵值(就好像上面说的,同一个通道的RGB相加必须等于1 => R+G+B=1)
desaturate: function() {
return this.add([
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, 0,
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, 0,
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, 0,
0, 0, 0, 1, 0
]);
},
//平均 返回灰色图片 是对红绿蓝进行加权平均数的计算
average: function( r, g, b ) {
if ( r === undefined ) r = 0.3333;
if ( g === undefined ) g = 0.3333;
if ( b === undefined ) b = 0.3334;
return this.add([
r, g, b, 0, 0,
r, g, b, 0, 0,
r, g, b, 0, 0,
0, 0, 0, 1, 0
]);
},
//明度 对红绿蓝进行数值偏移计算
brightness: function( r, g, b ) {
if ( g === undefined ) g = r;
if ( b === undefined ) b = r;
return this.add([
1, 0, 0, 0, r,
0, 1, 0, 0, g,
0, 0, 1, 0, b,
0, 0, 0, 1, 0
]);
},
//着色
colorize: function( rgb, alpha ) {
var r, g, b, inv_alpha;
if ( alpha === undefined ) alpha = 1;
//var alpha = alpha ? 0 : 1;
r = (((rgb >> 16) & 255) * 0.0039215686274509803921568627451); // / 255
g = (((rgb >> 8) & 255) * 0.0039215686274509803921568627451); // / 255
b = ((rgb & 255) * 0.0039215686274509803921568627451); // / 255
inv_alpha = 1 - alpha;
//console.log((rgb >> 8).toString(2))
//console.log((255).toString(2))
//console.log(((rgb >> 8) & 255).toString(2))
//console.log((inv_alpha + ((alpha * r) * FILTER.LUMA[0])), ((alpha * r) * FILTER.LUMA[1]), ((alpha * r) * FILTER.LUMA[2]), 0, 0)
//console.log(((alpha * g) * FILTER.LUMA[0]), (inv_alpha + ((alpha * g) * FILTER.LUMA[1])), ((alpha * g) * FILTER.LUMA[2]), 0, 0)
//console.log(((alpha * b) * FILTER.LUMA[0]), ((alpha * b) * FILTER.LUMA[1]), (inv_alpha + ((alpha * b) * FILTER.LUMA[2])), 0, 0)
return this.add([
(inv_alpha + ((alpha * r) * FILTER.LUMA[0])), ((alpha * r) * FILTER.LUMA[1]), ((alpha * r) * FILTER.LUMA[2]), 0, 0,
((alpha * g) * FILTER.LUMA[0]), (inv_alpha + ((alpha * g) * FILTER.LUMA[1])), ((alpha * g) * FILTER.LUMA[2]), 0, 0,
((alpha * b) * FILTER.LUMA[0]), ((alpha * b) * FILTER.LUMA[1]), (inv_alpha + ((alpha * b) * FILTER.LUMA[2])), 0, 0,
0, 0, 0, 1, 0
]);
},
//反向
invert: function( ) {
return this.add([
-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0
]);
},
invertRed: function( ) {
return this.add([
-1, 0, 0, 0, 255,
0, 1, 0, 0, 255,
0, 0, 1, 0, 255,
0, 0, 0, 1, 0
]);
},
invertGreen: function( ) {
return this.add([
1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, 1, 0, 255,
0, 0, 0, 1, 0
]);
},
invertBlue: function( ) {
return this.add([
1, 0, 0, 0, 255,
0, 1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0
]);
},
//alpha 反向
invertAlpha: function( ) {
return this.add([
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, -1, 255
]);
},
//饱和度
saturate: function( s ) {
var sInv, irlum, iglum, iblum;
sInv = (1 - s);
irlum = (sInv * FILTER.LUMA[0]);
iglum = (sInv * FILTER.LUMA[1]);
iblum = (sInv * FILTER.LUMA[2]);
return this.add([
(irlum + s), iglum, iblum, 0, 0,
irlum, (iglum + s), iblum, 0, 0,
irlum, iglum, (iblum + s), 0, 0,
0, 0, 0, 1, 0
]);
},
//对比度
contrast: function( r, g, b ) {
if ( g === undefined ) g = r;
if ( b === undefined ) b = r;
r += 1.0; g += 1.0; b += 1.0;
return this.add([
r, 0, 0, 0, (128 * (1 - r)),
0, g, 0, 0, (128 * (1 - g)),
0, 0, b, 0, (128 * (1 - b)),
0, 0, 0, 1, 0
]);
},
//快速校正对比度
quickContrastCorrection: function( contrast ) {
if ( contrast === undefined ) contrast = 1.2;
return this.add([
contrast, 0, 0, 0, 0,
0, contrast, 0, 0, 0,
0, 0, contrast, 0, 0,
0, 0, 0, 1, 0
]);
},
//色相调整 色环 0-360
adjustHue: function( degrees ) {
degrees *= FILTER.CONSTANTS.toRad;
var cos = Math.cos(degrees), sin = Math.sin(degrees);
return this.add([
((FILTER.LUMA[0] + (cos * (1 - FILTER.LUMA[0]))) + (sin * -(FILTER.LUMA[0]))), ((FILTER.LUMA[1] + (cos * -(FILTER.LUMA[1]))) + (sin * -(FILTER.LUMA[1]))), ((FILTER.LUMA[2] + (cos * -(FILTER.LUMA[2]))) + (sin * (1 - FILTER.LUMA[2]))), 0, 0,
((FILTER.LUMA[0] + (cos * -(FILTER.LUMA[0]))) + (sin * 0.143)), ((FILTER.LUMA[1] + (cos * (1 - FILTER.LUMA[1]))) + (sin * 0.14)), ((FILTER.LUMA[2] + (cos * -(FILTER.LUMA[2]))) + (sin * -0.283)), 0, 0,
((FILTER.LUMA[0] + (cos * -(FILTER.LUMA[0]))) + (sin * -((1 - FILTER.LUMA[0])))), ((FILTER.LUMA[1] + (cos * -(FILTER.LUMA[1]))) + (sin * FILTER.LUMA[1])), ((FILTER.LUMA[2] + (cos * (1 - FILTER.LUMA[2]))) + (sin * FILTER.LUMA[2])), 0, 0,
0, 0, 0, 1, 0
]);
},
//sepia 偏色 褐色调旧照片。
sepia: function( amount ) {
if ( amount === undefined ) amount = 0.5;
if ( amount > 1 ) amount = 1;
else if ( amount < 0 ) amount = 0;
return this.add([
1.0 - (0.607 * amount), 0.769 * amount, 0.189 * amount, 0, 0,
0.349 * amount, 1.0 - (0.314 * amount), 0.168 * amount, 0, 0,
0.272 * amount, 0.534 * amount, 1.0 - (0.869 * amount), 0, 0,
0, 0, 0, 1, 0
]);
},
//sepia2 偏色 红褐色调旧照片。
sepia2: function( amount ) {
if ( amount === undefined ) amount = 10;
if ( amount > 100 ) amount = 100;
amount *= 2.55;
return this.add([
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, 40,
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, 20,
FILTER.LUMA[0], FILTER.LUMA[1], FILTER.LUMA[2], 0, -amount,
0, 0, 0, 1, 0
]);
},
//灰白阈值
threshold: function( threshold, factor ) {
if ( factor === undefined ) factor = 256;
return this.add([
(FILTER.LUMA[0] * factor), (FILTER.LUMA[1] * factor), (FILTER.LUMA[2] * factor), 0, (-(factor-1) * threshold),
(FILTER.LUMA[0] * factor), (FILTER.LUMA[1] * factor), (FILTER.LUMA[2] * factor), 0, (-(factor-1) * threshold),
(FILTER.LUMA[0] * factor), (FILTER.LUMA[1] * factor), (FILTER.LUMA[2] * factor), 0, (-(factor-1) * threshold),
0, 0, 0, 1, 0
]);
},
//阈值 保留彩色
threshold_rgb: function( threshold, factor ) {
if ( factor === undefined ) factor = 256;
return this.add([
factor, 0, 0, 0, (-(factor-1) * threshold),
0, factor, 0, 0, (-(factor-1) * threshold),
0, 0, factor, 0, (-(factor-1) * threshold),
0, 0, 0, 1, 0
]);
},
/*threshold_alpha: function( threshold, factor ) {
if ( threshold === undefined ) threshold = 0.5;
if ( factor === undefined ) factor = 256;
return this.add([
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, factor, (-factor * threshold)
]);
},*/