控制台的艺术(附原理实现)

本文来自网易云社区

作者:黄锴

前言

艺术是孤独的产物,因为孤独比快乐更能丰富人的情感。 ——罗丹

不知道从什么时候开始,人们开始对美的追求从页面转移到了控制台,出现了很多有意思的控制台艺术:

人天生对美的追求,让其不满足于控制台只是黑白和ASCII码的世界,除了使用图案去吸引眼光,颜色也是非常常用的工具,色彩+图案,让原本无聊的控制台慢慢有了生机:

人都是视觉动物,喜欢美的事物,因此一个良好的交互,是非常重要的,在浏览器中,页面是主要的,控制台可能主要是用来吸引程序猿同类,提升程序猿同类对该公司的认同感(哎哟,不错哟,是我的菜)。但是在node环境中,没有页面,控制台就是交互唯一的入口,因此一个优美的控制台环境也是很重要的。


浏览器的样式设计

浏览器端比较简单,根据文档 console.log有一个格式化输出的选项(学过c语言的童鞋对格式化输出应该不陌生),其中有个选项%c就是设置输出样式(css样式,这样可定制化程度应该够用了吧)

占位符含义
%s字符串输出
%d or %i整数输出
%f浮点数输出
%o打印javascript对象,可以是整数、字符串以及JSON数据
%c样式


console.log("%cMy stylish message", "color: red; font-style: italic");console.log("%c3D Text", `text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);font-size:5em`);console.log('%cRainbow Text ', `background-image:-webkit-gradient( linear, left top, right top, color-stop(0, #f22),color-stop(0.15, #f2f), color-stop(0.3, #22f), color-stop(0.45, #2ff), color-stop(0.6, #2f2),color-stop(0.75, #2f2), color-stop(0.9, #ff2), color-stop(1, #f22) );color:transparent;-webkit-background-clip: text;font-size:5em;`);console.log("%c\n       ", `font-size:140px;background:url('http://o7bk1ffzo.bkt.clouddn.com/20180829153550853783124.png?imageMogr2/thumbnail/750x')no-repeat`);



当然初了样式,控制台还有很多实用的小技巧,例如追踪堆栈信息,分组输出,输出表格,计时,计次……可以参考 这个文章



Node 控制台样式设计

Node由于是运行在后台,它使用的是用户系统的控制台,无法利用浏览器便捷的渲染引擎,因此,它无法实现类似显示图(字符图如果算的话),阴影,边框这么复杂的样式,但是,小改改颜色什么的还是可以的。

毕竟清一色的黑白界面看久了还是会视觉疲劳的,所以现在的一些比较耗时的工具(npm,webpack,glup……)都会用颜色和简单的加载动画去优化用户体验,不让这个等待过程过于枯燥。想想,这和我们在浏览器端优化用户体验的思路不一样的吗?



控制台颜色

原理

不想看原理,想快速的使用的童鞋可以直接往下翻

其实node端的console是可以设置颜色的,这就涉及到linux的颜色转义序列了,最初接触这个东西是PS1中修改字体颜色,其实就是用它来控制linux下控制台输出颜色的,熟悉的人可能还记得:

\e[F;Bm 输入字符串\e[0m

\e 转义字符开始,ESC 的 ASCII 码用十进制表示就是 27,等于用八进制表示的 033。

开始定义颜色。

F' 为字体颜色,编号30~37

B为背景色,编号40~47。

O 为特殊意义代码

m 是标记,后面不用跟空格

前景后景颜色
3040黑色
3141红色
3242绿色
3343黄色
3444蓝色
3545洋红
3646青色
3747白色

举例,现在你可以直接在控制台输入:

echo -e "\e[37;41m 网易云音乐 \e[0m"


那么接下来,用console来输出颜色就很简单了(注意\x1B就是十六进制的27),借用网上的一个样式表:

console.log('\x1B[31m%s\x1B[0m', '错误');  
console.log('\x1B[33m%s\x1b[0m:', '警告');  
var styles = {    'bold'          : ['\x1B[1m',  '\x1B[22m'],    'italic'        : ['\x1B[3m',  '\x1B[23m'],    'underline'     : ['\x1B[4m',  '\x1B[24m'],    'inverse'       : ['\x1B[7m',  '\x1B[27m'],    'strikethrough' : ['\x1B[9m',  '\x1B[29m'],    'white'         : ['\x1B[37m', '\x1B[39m'],    'grey'          : ['\x1B[90m', '\x1B[39m'],    'black'         : ['\x1B[30m', '\x1B[39m'],    'blue'          : ['\x1B[34m', '\x1B[39m'],    'cyan'          : ['\x1B[36m', '\x1B[39m'],    'green'         : ['\x1B[32m', '\x1B[39m'],    'magenta'       : ['\x1B[35m', '\x1B[39m'],    'red'           : ['\x1B[31m', '\x1B[39m'],    'yellow'        : ['\x1B[33m', '\x1B[39m'],    'whiteBG'       : ['\x1B[47m', '\x1B[49m'],    'greyBG'        : ['\x1B[49;5;8m', '\x1B[49m'],    'blackBG'       : ['\x1B[40m', '\x1B[49m'],    'blueBG'        : ['\x1B[44m', '\x1B[49m'],    'cyanBG'        : ['\x1B[46m', '\x1B[49m'],    'greenBG'       : ['\x1B[42m', '\x1B[49m'],    'magentaBG'     : ['\x1B[45m', '\x1B[49m'],    'redBG'         : ['\x1B[41m', '\x1B[49m'],    'yellowBG'      : ['\x1B[43m', '\x1B[49m']


成熟工具包

当然,为了实现更加丰富多彩的颜色,还是建议使用现成的包(根据自己的喜欢选择,我选择chalk)

  • colors :可以实现很复杂的颜色样式

var colors = require('colors');console.log('hello'.green); // outputs green textconsole.log('i like cake and pies'.underline.red) // outputs red underlined textconsole.log('inverse the color'.inverse); // inverses the colorconsole.log('OMG Rainbows!'.rainbow); // rainbowconsole.log('Run the trap'.trap); // Drops the bass

  • chalk 支持rgb256真彩色,并且写法我更喜欢。

const chalk = require('chalk');const log = console.log;// Combine styled and normal stringslog(chalk.blue('Hello') + ' World' + chalk.red('!'));// ES2015 template literallog(`
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`);// ES2015 tagged template literallog(chalk`
CPU: {red ${cpu.totalPercent}%}
RAM: {green ${ram.used / ram.total * 100}%}
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
`);


加载动画

介绍

我记得大学学C语言的时候,就有一个作业是做控制台动画(正弦余弦波型),还有做控制台游戏“弹球”,就通过利用命令清屏,然后一帧帧重绘出来。

system ('cls')

其实控制台动画,早在1997年就出现了,不知道有没有人看过这个,控制台版的星球大战:

 # telnet towel.blinkenlights.nl



还有这个小sl命令的小汽车:

# apt-get install sl         (In Debian like OS)
# yum -y install sl         (In Red Hat like OS)
# brew install sl  (Max Os)


(如果你想尝试更多,可以参考这篇文章:《20 Funny Commands of Linux or Linux is Fun in Terminal》)


原理

回到我们这边来,node后台处理经常会需要长时间运行的业务(下载远程数据,打包……)这时候,如果这时候控制台直接卡死,用户体验会很糟糕,用户也不知道现在到底在干嘛,所以给一个加载动画是很有必要的。

加载动画的原理也很简单,让我们的数据实在在同一行输出,通过一帧帧图案的替换,实现动画。下面我写了一个简单的实现加载动画和进度条的demo,可以体验一下:

const readline = require("readline");const char = ["◐", "◓", "◑", "◒"];let cur = 0;let info = "loading...";let bar = "[----------------------------]";let done = false;// setInterval(() => {//   //删除光标所在行//   readline.clearLine(process.stdout);//   //移动光标到行首//   readline.cursorTo(process.stdout, 0);//   cur = (cur + 1) % char.length;//   process.stdout.write(char[cur] + " loading...", "utf-8");// }, 200);let timer = setInterval(() => {  //删除光标所在行;
  readline.clearLine(process.stdout);  //移动光标到行首
  readline.cursorTo(process.stdout, 0);  if (done) {
    info = "done!";    // clearInterval(timer);
  }
  process.stdout.write(`${bar} ${info}`, "utf-8");
  bar = bar.replace("-", "="); //利用每次只替换一个的原理
  if (bar.indexOf("-") < 0) {
    done = true;
  }
}, 200);

当然,你也可以用single-line-log 去处理。不过现在现成的加载动画的包也很多,就没必要自己手动写了:


成熟的工具包

  • ora:halk的作者,不愧是控制台艺术的完美饯行者,刚说完他的chalk,又要提他的ora ,这是一个控制台加载动画的包。用法也非常简单(通过start()控制开始,然后在需要结束的时候触发succeed/fail/warn/info函数即可。

const ora = require('ora');const spinner = ora('Loading unicorns').start();
……
spinner.succeed([text])
……

  • Node.CLI-Progress 这是一个处理进度条的组件,原理和我上面写的都大同小异,不过包装的十分完善了,感兴趣可以去看看。



字符画(ASCII Art)

介绍

字符画,英文名:zi fu hua~ 开玩笑,字符画专业词汇叫:ASCII Art,最早于1982年(早我出生10年)诞生于卡内基梅隆大学,根据WIkiPedia的解释:

ASCII艺术,又名“文字图”、“字符画”、“文字画”,这种主要依靠计算机表现的艺术形式是指使用计算机字符(主要是ASCII)来表达图片,最早于1982年美国卡内基梅隆大学出>> > 现,互联网刚出现时在英语世界的社交网(Usenet、BITNET、网络论坛、FidoNet、电子布告栏系统BBS)上时常利用到的表情符号。它可以由文本编辑器生成。很多ASCII艺术> 要求使用定宽字体(固定宽度的字体,例如在传统打字机上使用的字体)来显示。

从:-) 到 下面这些例子,都是字符画的表现形式:

    HHHHHH    HHHHHH     IIIIII     
     HHHH      HHHH       IIII      
     HHHH      HHHH       IIII      
     HHHHHHHHHHHHHH       IIII      
     HHHHHHHHHHHHHH       IIII     
     HHHHHHHHHHHHHH       IIII       
     HHHH      HHHH       IIII      
     HHHH      HHHH       IIII     
     HHHH      HHHH       IIII     
    HHHHHH    HHHHHH     IIIIII


成熟工具

这种艺术,如果只凭我们手敲控制,可谓难于上青天,所以需要借助一些现成的生成工具:

  • Text to ASCII Art Generator 这是一个文字转ASCII码图案的工具,有很多工具的图案就是用这个生成的

  • Ascii Art Generator 这网站可以把图片转换成ASCII码图案,可以生成纯字符的,也可以生成带颜色字符的,并且可以生成一个

    html给你。

    ……(这类网站实在太多,功能都大同小异)


交互操作

除了显示外,我们还可以加入一些交互式的操作,这个就更复杂了,还好有inquirer 。它提供了很多交互式的操作,关键是它提供了很多插件:单选,时间选择,自动不全,建议,它还有和chalk配合使用的动态字体换色……

反正,这一款足以强大到编写你想要的交互式操作,建立属于自己的CLI!




最后

不管做什么,当你不是把它当作一个机械产物,而是用美的眼光去看待,这件物件便有了温度。

前端作为一个富有创造性和艺术性的职业,虽然不一定能像画家一样做一个美好视觉体验的创造者,但是至少可以当一个美好视觉体验的传递者。

配合颜色和加载字符动画,或者字符画,你的控制台将不再那么单调,至少,让使用者觉得舒服,以下是我现在做的项目的大概输出:





网易云大礼包:https://www.163yun.com/gift

本文来自网易云社区,经作者黄锴授权发布


相关文章:
【推荐】 Hi,这有一份风控体系建设干货
【推荐】 深入浅出“跨视图粒度计算”--2、INCLUDE表达式

posted @ 2018-09-07 10:06  网易数帆  阅读(1902)  评论(0编辑  收藏  举报