美文网首页基础前端
认识下 ANSI ,写个简版 progress

认识下 ANSI ,写个简版 progress

作者: CondorHero | 来源:发表于2021-11-17 22:34 被阅读0次

背景 :在上一篇文章 认识下 ANIS,写个简版 chalk 我们系统的学习了 ANSI 关于颜色的知识,并简单实现了一个 chalk,然而 ANSI 剩下的一大部分关于光标的我们没有讲,这次我们就来学习 ANSI 关于光标的知识,并简单的实现一个 progress

目录

  • ANSI 光标
    • 进度指示器
  • progress 的实现

ANSI 光标

关于 ANSI 的基本语法就不讲了,可以去看 认识下 ANIS,写个简版 chalk 里面有非常详细的讲解,直接进入光标功能讲解。

关于光标移动操作最常见的是下面四个:

编码 ANSI 名称 作用
{n} A \u001b[{n}A 光标上移(Cursor Up) 光标向指定的方向移动 n(默认1)格。如果光标已在屏幕边缘,则无效。
{n} B \u001b[{n}B 光标下移(Cursor Down) 光标向指定的方向移动 n(默认1)格。如果光标已在屏幕边缘,则无效。
{n} C \u001b[{n}C 光标前移(Cursor Forward) 光标向指定的方向移动 n(默认1)格。如果光标已在屏幕边缘,则无效。
{n} D \u001b[{n}D 光标后移(Cursor Back) 光标向指定的方向移动 n(默认1)格。如果光标已在屏幕边缘,则无效。

为了,更好的观察光标移动的现在,我们先来个小案例热身下。在使用 Webpack 等打包工具,我们启用服务的时候,一般都会有进度指示器,从 0% ~ 100% 从而给我们个心理预期。现在就来实现它。

进度指示器

思路,要想实现进度指示器,首先要搞定数字的事情,这个简单,一个循序搞定:

for(let i  = 0; i <= 100; i++) {
  console.log(i);
}

运行程序控制台换行顺序输出结果:

第二步的思路很关键:

  1. 程序输出 1 结束的时候,我们应该把光标拉到行的开头,重写当前行,数字为 2。
  2. 程序输出 2 结束的时候,我们应该把光标拉到行的开头,重写当前行,数字为 3。
  3. 以此类推
  4. 程序输出 99 结束的时候,我们应该把光标拉到行的开头,重写当前行,数字为 100。

总结就两个步骤:

  1. 前移动光标。
  2. 从光标开始,重写当前行。

前移动光标我们使用 \u001b[{n}D。重写行我们使用 NodeJs 的 process.stderr.write

好了,结果输出最长位为 100% 四位,为了保证光标能前移到行首,我们给的 n = 4

// 同步睡眠函数
const asyncSleep = (ms) => {
  const startTime = Date.now();
  let nowTime = startTime;
  while (nowTime - startTime < ms) {
    nowTime = Date.now();
    continue;
  }
}

const loading = () => {
  for(let i = 0; i <= 100; i++) {
    asyncSleep(1000)
    // 光标前移四格
    process.stderr.write("\u001b[4D");

    asyncSleep(1000)
    process.stderr.write(`${i}%`);
  }
}
loading()

运行程序,中间加了睡眠函数,来模拟加载或打包过程,能很直观的看到光标和重写的变化,动图只录制了十秒:

2021-11-17 17-11-01.2021-11-17 17_12_21.gif

上面只是为了给你观察光标的移动,我们可以简单修改下代码,移动光标立刻重写,以达到完全模拟进度提示。

// 改了 loading 的代码
const loading = () => {
  for(let i = 0; i <= 100; i++) {
    asyncSleep(100)
    // 光标前移四格
    process.stderr.write(`\u001b[4D${i}%`);
  }
}

看下完美的运行效果:

2021-11-17 17-18-07.2021-11-17 17_18_36.gif

刺激不,看到百分比在函数执行完之前从1%到无缝变化100%

OK,我们学会了前移操作,那上下后移,甚至清行、清屏对你来说也是小意思了。

Wiki 上维护了一份不完整的 ANSI 控制序列列表如下,大胆的发挥你的想象去使用吧。

progress 的实现

上面的百分比实现有点简单,接下来我们实现一个难点的,实现一个类似 progress 的 barLoading。

2021-11-16 17-35-45.2021-11-16 17_36_09.gif

代码不难写重要的是思路,我们来搞下思路:

  1. 光标定位到行首
  2. 进度条的长度是函数的参数。
  3. 进度条的长度渲染的是空格。
  4. 我们给空格加上背景色就完成了。

好了,我们来看代码:


// 同步延时器
const asyncSleep = (ms) => {
  const startTime = Date.now();
  let nowTime = startTime;
  while (nowTime - startTime < ms) {
    nowTime = Date.now();
    continue;
  }
}

const barProgress = (total) => {
  for(let i = 0; i <= total; i++) {
    // 1. 光标定位到行首
    process.stderr.write(`\u001b[${total + 100}D`);

    // 2. 睡眠 100ms
    asyncSleep(100);
    let left = i, right = total - i, whiteSpace = "", greenSpace = "";
    
    // 加载完成的空格数
    while (left) {
      greenSpace += " ";
      left--;
    }

    // 未加载的空格数
    while (right) {
      whiteSpace += " ";
      right--;
    }
    // 填充背景色
    const leftBg = `\u001b[42m${greenSpace}\u001b[49m`;
    const rightBg = `\u001b[47m${whiteSpace}\u001b[49m`;

    // 输出终端
    process.stderr.write(`${leftBg}${rightBg}${i}%`);
  }
}

使用的时候直接调用就行了,比如说:barProgress(20);,你可以随便改变传入的参数来决定 progress 的长度。

除了我们演示的 progress 我们还知道一种井号进度条,有时候安装 npm 包的时候会出现,演示效果如下:

2021-11-17 18-57-40.2021-11-17 18_58_34.gif

这个实现很简单了,去掉颜色填充,左边填充变井号,右边仍然保持空格,立刻就能见到效果。

const poundProgress = (total) => {
  for(let i = 0; i <= total; i++) {
    // 1. 光标定位到行首
    process.stderr.write(`\u001b[${total + 100}D`);

    // 2. 睡眠 100ms
    asyncSleep(50);
    let left = i, right = total - i, whiteSpace = "", greenSpace = "";
    
    // 加载完成的空格数
    while (left) {
      greenSpace += "#";
      left--;
    }

    // 未加载的空格数
    while (right) {
      whiteSpace += " ";
      right--;
    }
    
    // 输出终端
    process.stderr.write(`[${greenSpace}${whiteSpace}]${i}%`);
  }
}

poundProgress(100);

掌握了精髓我们想怎么玩就怎么玩,比如五角星的 progress:

2021-11-17 22-22-38.2021-11-17 22_23_22.gif

总结

基于上篇文章 认识下 ANIS,写个简版 chalk 留的知识点,我们系统学习了光标的用法,并带大家实践了平时常见的加载器和 progress,行文最后推荐个基于 ANSI 实现的 npm 包,ansi-escape-sequences

参考

一些ANSI控制序列(不完整列表)

相关文章

网友评论

    本文标题:认识下 ANSI ,写个简版 progress

    本文链接:https://www.haomeiwen.com/subject/zanltrtx.html