Node.js 中有不少常用的 Color 模块,例如 chalk、colors.js、cli-color 等,通过这些模块我们输出各种带颜色、方面区分或者更酷的日志以及 CLI 工具提示。那么今天带大家简单了解一下 Color 模块的实现。
ANSI escape code
与前端上对元素内的文字加上了 CSS 修饰一样。terminal 中输出的文字包含颜色也是因为文字的数据跟随了颜色描述的数据。而要了解 terminal 上的颜色,首先需要了解 ANSI escape code。颜色修饰数据对于 terminal 而言,是跟 "\n" 类似的让显示出现变化的一种转义字符。与常见的转义字符不同,修饰颜色字符(在大部分平台上)按照 CSI codes 的格式以 "ESC" + "[" 字符开头,形如:ESC[ + code1;code2;...;codeN + m 结束(其中的 code 即修饰颜色的数据)。例如:
echo -e "\033[31;42mhello"; # 控制字符[红色文字;绿色背景结束符号hello
echo -e "\033[0m"; # 重置颜色修饰
echo -e "\033[33mworld"; # 控制字符[黄色文字结束符号world
echo -e "\033[0m"; # 重置颜色修饰
各位用户(win除外)可以在 terminal 上尝试一下执行效果,或者使用 Node.js (包括win)运行以下代码试试:
console.log("\033[31;42mhello"); // 控制字符[红色文字;绿色背景结束符号hello
console.log("\033[0m"); // 重置颜色修饰
console.log("\033[33mworld"); // 控制字符[黄色文字结束符号world
console.log("\033[0m"); // 重置颜色修饰
执行效果:
<img src="https://pic1.zhimg.com/v2-f9eb6f53577b3faa27af8cf1f20a13d8_b.png" data-rawwidth="506" data-rawheight="83" class="origin_image zh-lightbox-thumb" width="506" data-original="https://pic1.zhimg.com/v2-f9eb6f53577b3faa27af8cf1f20a13d8_r.png">注意:ESC 转义字符在 ASCII 码中十进制是 27,八进制是 033,十六进制是 0x1B。所以在 Node.js 中除了
注意:ESC 转义字符在 ASCII 码中十进制是 27,八进制是 033,十六进制是 0x1B。所以在
Node.js 中除了 \033 之外还可以写成 \u001b (严格模式强制)。
ANSI colors and styles
ANSI 的标准中,特定的 code 表示特定的含义,对于 terminal 的颜色主要有三方面的定义。分别是样式、颜色、明亮度。
样式
常见的字体样式分别是(支持情况视 terminal 而定):
<img src="https://pic3.zhimg.com/v2-fc64307c986a59985873aa474f7a3afa_b.png" data-rawwidth="399" data-rawheight="276" class="content_image" width="399">
颜色 & 明亮
code 30 - 37 为字体颜色,91 - 97 为字体颜色的明亮版。40 - 47 为背景色,100 - 107 为背景色的明亮版。terminal 的默认字体色 code 是 39,默认背景色是 49。
<img src="https://pic2.zhimg.com/v2-88db5cf4304f609fed85b4d08b7316d9_b.png" data-rawwidth="550" data-rawheight="147" class="origin_image zh-lightbox-thumb" width="550" data-original="https://pic2.zhimg.com/v2-88db5cf4304f609fed85b4d08b7316d9_r.png">完整的 code 信息参见
完整的 code 信息参见 CSI codes中的
SGR (Select Graphic Rendition) parameters。
Color 模块实现
了解了 ANSI 编码中关于颜色部分的知识之后,在 Node.js 中实现一个颜色模块就很简单了。
首先我们可以明确一点,跟某段输出的内容加上颜色,其实要做的事情就是将该内容使用修饰颜色的数据包起来即可。例如输出一段绿底红字,相当于输出 “绿色背景红色文字修饰数据” + “输出文字” + “重置修饰”。其效果可以简单这样写:
function getRedTextGreenBg(text) {
return '\033[31;42m' + text + '\033[0m';
}
console.log(getRedTextGreenBg('hello world'));
如果了解 CSI codes 的规则就会发现,对于颜色数据 ESC[ + code1;code2;...;codeN + m 的写法其实和 ESC[ + code1 + m + ESC[ + code2 + m + ... + ESC[ + codeN + m 的效果是一样的,目前 Node.js 中大部分 Color 模块都是按照后者的情况来实现的。
完整一点的 styles 数据可以参考 chalk 的 ansi-styles。那么我这边换个简单的思路模仿 chalk 的调用方式来实现一版,供各位大佬围观:
'use strict';
// 严格模式要用 unicode 的十六进制不能用八进制的 \033
const styles = {
// style: [ style code, reset code ]
'bold': ['\u001b[1m', '\u001b[22m'],
// ...
'black': ['\u001b[30m', '\u001b[39m'],
'red': ['\u001b[31m', '\u001b[39m'],
'green': ['\u001b[32m', '\u001b[39m'],
'yellow': ['\u001b[33m', '\u001b[39m'],
// ...
'bgBlack': ['\u001b[40m', '\u001b[49m'],
'bgRed': ['\u001b[41m', '\u001b[49m'],
'bgGreen': ['\u001b[42m', '\u001b[49m'],
'bgYellow': ['\u001b[43m', '\u001b[49m'],
// ...
};
const color = {};
Object.keys(styles).map((key) =>
color[key] = (text) =>
styles[key][0] + text + styles[key][1]);
console.log(
color.bgGreen(color.red('hello,')) +
color.yellow(' world!'));
执行效果:
<img src="https://pic3.zhimg.com/v2-e3ccb4eb7ebfa3676b304bce1cb6a08a_b.png" data-rawwidth="1480" data-rawheight="528" class="origin_image zh-lightbox-thumb" width="1480" data-original="https://pic3.zhimg.com/v2-e3ccb4eb7ebfa3676b304bce1cb6a08a_r.png">