摘要
你知道终端上打印的彩色文本是怎样实现的吗?本文主要介绍控制台上关于彩色字符的格式构成规则,并列举一些简单的示例以供参考理解。
ANSI Escape Code
是一种带内信令(In-band signaling)的转义序列标准,用于控制文本在终端上的光标位置、颜色和其他选项。在文本中嵌入确定的字节序列,大部分以 ESC 转义字符和 “[” 字符开始,终端会把这些字节序列解释为相应的指令,而不是普通的字符编码。ANSI 转义序列中以
ESC [
(即\x1b[
) 开头的叫作 Control Sequence Introducer,简写为 CSI。终端会将 CSI 字节序列解释为具有相应含义的指令。以 CSI 开头的指令有很多,大致可分四类:光标移动指令、清屏指令、字符渲染(Graphic Rendition)指令和终端控制指令。
基于 CSI
控制指令可以实现的功能是非常丰富的。如果你使用过支持 telnet 的 BBS 论坛的话,应该会有所了解。例如,你可以在终端中执行如下命令,以 telnet 的方式访问水木社区体验一下其魅力:
telnet bbs.newsmth.net
1 ANSI colors
颜色设置控制符号
CSI
指令中有颜色相关的控制指令字符。按照表示颜色的形式和范围的不同,可将它们分为三类:
3/4
位颜色表示法8
位颜色表示法24
位颜色表示法, 支持RGB
色值
ANSI 转义字符序列的控制字符范围及含义,可以查阅维基百科了解详情: ANSI转义序列
在 npmjs
上有许多基于ANSI color styles
指令封装的控制台颜色格式化工具包,如:
在这些工具库中,只有 chalk
实现了全部ANSI colors
颜色设置的接口封装支持,并将其作为独立 npm 包 ansi-styles 进行维护(chalk 5
项目则将 ansi-styles
的主 js 文件进行了内置)。
2 3/4
位颜色表示法
基本格式为: \x1b[< N>m[字符]\x1b[< M>m
其中 N
和 M
分别表示控制字符和结束字符,具体为:
- 前景色(彩色文本):
\x1b[< N>m[字符]\x1b[39m
。其中M=39
,N 取值范围30<=n<=37
- 背景色(彩色背景):
\x1b[< N>m[字符]\x1b[49m
。其中M=49
,N 取值范围40<=n<=47
示例:
console.log('\x1b[92m\x1b[4m 那些听不见音乐的人以为跳舞的人疯了。\x1b[0m'); console.log('\x1b[32m 万事开头难,然后中间难,最后结尾难 \x1b[39m');
3/4
位颜色表示法在各种终端类型中拥有最为广泛的支持,故常见的针对终端彩色文本格式化的工具库,基本均基于此语法规则而封装。
3 8
位颜色表示法
基本格式为: \x1b[< 38|48>;5;< N>m[字符]\x1b[0m
其中38
表示选择前景色,48
表示选择背景色,N
取值范围0~255
,可表示 256 种颜色。末尾的\x1b[0m
表示重置设置。
示例:
console.log('\x1b[38;5;129m最近手头有点紧,想借你的手牵一牵\x1b[0m');
ANSI colors
8位表示法全部前景色示例:
function printColors256() { const colorList = ['']; const bgcolorList = ['']; let idx = -1; while(idx++ < 255) { const text = String(idx).padEnd(3, ' '); const br = !idx || (idx + 1) % 16 ? '' : '\n'; colorList.push(`\x1b[38;5;${idx}m${text}\x1b[0m ${br}`); bgcolorList.push(`\x1b[48;5;${idx}m${text}\x1b[0m ${br}`); } console.log(`ANSI 256 colors:\n${ colorList.join('') }`); console.log(`ANSI 256 background colors:\n${bgcolorList.join('')}`); } printColors256();
ANSI colors 256
示例:
function rainbow(words, startPos = Math.ceil(Math.random() * 100)) { // const colorCodes = new Array(256).fill().map(() => Math.floor(Math.random() * 255)); const colorCodes = [196, 214, 45, 128, 226, 207, 42, 205, 112, 39, 122, 227, 111]; const str = words .split(' ') .map(word => word .split('') .map((ch, idx) => `\x1b[38;5;${colorCodes[(idx + startPos) % colorCodes.length]}m${ch}\x1b[0m`) .join('') ) .join(' '); console.log(str); } rainbow('Her wishful face haunts my dreams like the rain at night.');
4 24
位颜色表示法
基本格式为: \x1b[< 38|48>;2;< R>;< G>;< B>m[字符]\x1b[0m
其中38
表示选择前景色,48
表示选择背景色。R G B
表示三原色,取值范围分别都是 0~255
。
示例:
console.log('\x1b[38;2;25;154;48m透过你的眼 \x1b[38;2;255;154;48m离别也是诗 \x1b[0m'); console.log('\x1b[48;2;48;154;255m \x1b[38;2;255;255;255m 偶尔还是需要出去走一走,才知道躺在床上多么舒服 \x1b[0m');
RGB
模式可以有 1600+
万种颜色组合,可以支持的色彩相当丰富了。但并不是所有的终端都支持该模式。
值得一提的是,从 Chrome 69 开始,其浏览器控制台增加了 ANSI Escape colors 的编码指令支持。经过测试,其控制台支持 3/4
位表示法和24
位表示法的完整格式。你可以复制上面的相关例子,在 Chrome Console 控制台测试体验。
5 扩展参考
关于 ANSI Escape Code
相关基础知识的细节,如有兴趣可参阅以下链接中的文档或文章进一步了解: