1 .开始
做前端的避免不了和颜色值打交道,某天ui给我们了一个颜色#87cefaff
,交代全部风格给我用这个颜色,但是出于代码的统一我们需要用rgba的形式进行代码的书写,于是我们send消息给ui表示我需要这个色号的rgba值,ui做完图就是划水时间了,哪有闲工夫再管你的破事,消息最终是已读未回。。。。最终我们还是要亲自面向百度来查找相关的网站进行解析。闲话说完,今天我们就来实现下在开发中,我们需要打交道最多的两个颜色表示形式(RGBA、HEX)的转换,PS:实现很简单,主要来复习下这两种色号的理解。我们先来粘上最终的实现效果:
在开始前,我们先来介绍下RGBA和HEX:
-
RGBA是代表Red(红色)Green(绿色)Blue(蓝色)和Alpha的色彩空间。虽然它有的时候被描述为一个颜色空间,但是它其实仅仅是RGB模型的附加了额外的信息。采用的颜色是RGB,可以属于任何一种RGB颜色空间,但是Catmull和Smith在1971至1972年间提出了这个不可或缺的alpha数值,使得alpha渲染和alpha合成变得可能。提出者以alpha来命名是源于经典的线性插值方程αA + (1-α)B所用的就是这个希腊字母。
-
Hex代码为“三字节”的十六进制数字,其中每一字节或每两个字符分别代表红、绿、蓝颜色的强度。 #XXXXXX Hex代码字节值的范围从颜色强度最低的00到最高强度FF。 举例来说,白色由最高强度的三基色混合而成,其Hex颜色代码即为#FFFFFF。
这里我们来举例说明下我们需要重点关注的知识点。先说rgba,我们拿红色字号RGBA(255,0,0,1)
来说,255就是R也就是red的缩写,后面的两个0分别是G(green的缩写)和B(blue的缩写),他们的取值范围是0-255,可以简单理解为R、G、B的值越大,他的颜色就越纯,最后的那个1就是A(alpha的缩写),表示透明度,取值范围是0-1,可以理解为值越大不透明度也就越高,最终他们混的的结果就是我们所看到的颜色了。
可能开发中我们使用更多的是rgb,不关注最后的a,这里来举个例子分享下a值的用法之一:实现一个卡片,并且背景是天蓝色半透明。一眼看去,这是简单么,我直接background:skyblue;opacity:0.5
,打完收工,我们来看下效果:
欸,这不对吧,我的文字怎么也变透明了,所以这里使用opacity是不正确的,它会使整个box区域透明,所以RGBA中的A就发挥作用了,只需要将上面的代码改为background:rgba(135, 206, 250, 0.5)
,就完事了,这里我们看下效果
说完RGBA我们再来浅聊下HEX,我们直接反推,还拿红色RGBA(255,0,0,1)
来说,它对应的hex值是#ff0000ff
,这里我们可以更清晰的观察到,R 255对应的16进制是ff、G 0对应的16进制是00、B 0对应的16进制是00、A 1*255 对应的16进制是ff,所以我们可以把hex值两两分割 ff 00 00 ff,也就是rgba对应的16进制(A需要乘255)。这里可能会有人说,不对啊这个hex值我写成#f00也是红色啊,这也是hex的另外一个特点,缩写,根据上面的规律,我们写#000000和写#000】、#ffffff和写#fff都是一样的,我们常见的hex长度也是3位、6位和8位。
2. 实现
通过上面的分析,实现也就很明了了,其实就是16进制和10进制的转换,看到这里其实你就可以自己先去实现下了。下面我们主要来实现两个方法rgbaToHex
和hexToRgba
,在此之前我们要知道在js中如何进行10进制和16进制的转换,我们需要用到两个方法:
-
parseInt(string, radix) parseInt - JavaScript | MDN (mozilla.org),16进制转10进制,例如:
parseInt('ff', 16) -> 255
- string : 要被解析的值。如果参数不是一个字符串,则将其转换为字符串 (使用
ToString
抽象操作)。字符串开头的空白符将会被忽略。 - radix: 从
2
到36
的整数,表示进制的基数。例如指定16
表示被解析值是十六进制数。如果超出这个范围,将返回NaN
。假如指定0
或未指定,基数将会根据字符串的值进行推算。注意,推算的结果不会永远是默认值10
!文章后面的描述解释了当参数radix
不传时该函数的具体行为。
- string : 要被解析的值。如果参数不是一个字符串,则将其转换为字符串 (使用
-
Number.toString(radix)Number.prototype.toString() - JavaScript | MDN (mozilla.org),10进制转16进制,例如:
(255).toString(16) -> 'ff'
- radix: 一个整数,范围在
2
到36
之间,用于指定表示数字值的基数。默认为 10。
- radix: 一个整数,范围在
下面贴上实现代码:
- hexToRgba,将hex颜色代码转成rgba颜色代码
// hex转rgba,本质是将16进制转成10进制
const hexToRgba = (hexStr: string): string => {
// 去除#号
let excludePrefix = hexStr.replace("#", "");
// hex值的长度
let hexStrLength = excludePrefix.length;
// hex的格式 #bfa #bbffaa #bbffaaff(最后的ff表示透明度),这里判断是正确的格式
if (hexStr.startsWith("#") && (hexStrLength == 3 || hexStrLength == 6 || hexStrLength == 8)) {
let hexArr = [];
// 最终的rgba字符串
let rgbaStr = "";
// 针对不同格式分别处理
switch (hexStrLength) {
// 如果是3位
case 3:
rgbaStr = `rgba(${excludePrefix
.split("")
.map(hex => parseInt(hex.repeat(2), 16))
.join(", ")}, 1)`;
break;
// 如果是6位
case 6:
for (let i = 0; i < 3; i++) {
hexArr.push(excludePrefix.slice(i * 2, i * 2 + 2));
}
rgbaStr = `rgba(${hexArr.map(hex => parseInt(hex, 16)).join(", ")}, 1)`;
break;
// 如果是8位
case 8:
for (let i = 0; i < 4; i++) {
hexArr.push(excludePrefix.slice(i * 2, i * 2 + 2));
}
rgbaStr = `rgba(${hexArr
.map((hex, index) => index < 3 && parseInt(hex, 16))
.filter(item => item)
.join(", ")}, ${(parseInt(hexArr[3], 16) / 255).toFixed(2)})`;
break;
}
return rgbaStr;
} else {
message.error("请输入正确的hex值");
return "";
}
};
- rgbaToHex,将rgba颜色代码转成hex颜色代码
// rgba转hex,本质是将10进制转成16进制
const rgbaToHex = (rgbaStr: string): string => {
// 匹配逗号分割的数字,join(".")是为了处理小数
let rgbaArr = rgbaStr.split(",").map(item => item.match(/\d+/g)!.join("."));
// 最终的hex字符串
let hexStr = "#";
// 这里判断是正确的格式
if (rgbaArr.length == 3 || rgbaArr.length == 4) {
hexStr += rgbaArr
.map((item, index) => {
// 如果r,g,b任何一个是0,转成16进制后把0重复两遍
let str =
index < 3
? Math.floor(Number(item)).toString(16)
: Math.floor(Number(item) * 255).toString(16);
return str.length == 1 ? str.repeat(2) : str;
})
.join("");
return hexStr;
} else {
message.error("请输入正确的rgba值");
return "";
}
};
两个主要方法实现完之后,再写出自己喜欢的样式,我们的功能就完成啦!