余烬缀记

开发一个终端动态多行输出的工具

edited on:

wallhaven-k9xj71

要开发动态在终端输出就需要用到 NodeJS 自带的readlineprocess.stdout

主要用到readline中的clearLinemoveCursorcursorTo这三个函数和process.stdout.write这个输出函数

仅仅单行动态输出只需要简单的清除目标行然后输出就可以了,多行却很复杂,不知道是不是我想得太复杂了

现在主要目的是实现一个如下方所展示的打印格式

Program: xxx
Initializing
Program: xxx
Loading: ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮                                              ▮ 40%
Program: xxx
Processing: ▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮▮                                              ▮ 40%
Program: xxx
Completed, Time: 3ms

上方的进度条主要是使用字符串重复实现

export function progress(p: number, pad: number = 0) {
  p = Math.min(100, p)
  const columns = process.stdout.columns - 8 - pad
  const pr = Math.floor((columns / 100) * p)
  const v = ``.repeat(pr)
  const s = ' '.repeat(Math.max(0, columns - pr))
  return `${v}${s}${p.toString().padStart(2, '0')}%`
}

下面将用到string-widthchalk两个 npm 包,一个计算字符串宽度一个输出颜色,此外由于光标走错一行就会打乱渲染,为了处理这个问题所以需要暂停查看光标所在位置,在异步函数中可以同步一个定时器来实现暂停

export async function wait(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms))
}

要实现动态更新终端上的每行就必需记录每次输出的数据,此外还要有不变的 ID、数据所在行和时间,使用interface来表示数据结构

interface LogItem{
  id: Symbol // 用Symbol来作为唯一ID
  index: number // 记录当前数据所在行
  length: number // 记录数据的字节数,搭配string-width和process.stdout.columns可以实现换行
  line: number // 用于记录数据存在的换行符
  renderer: boolean // 第一次渲染和第二次渲染有所差异
  time: number // 记录时间,用于顺序渲染,防止输出混乱
}

要实现动态输出主要是控制光标和存储输出数据和计算输出数据中换行符号

对应源码:https://github.com/tonitrnel/subtitle-translation-tool/blob/master/src/utils/dynamic-terminal.util.ts