We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
经常用系统终端进行技术开发的过程会看到一些终端的动画效果,例如这个
最近刚好好奇的了解了一下,才知道实现这个能力是利用了终端支持的 ANSI转义序列,任何语言只要能调用终端的标准输入/输出(stdin/stdout),都可以直接使用ANSI转义序列的规范制作对应的终端动画。
ANSI转义序列
stdin/stdout
ANSI是一种字符代码,为使计算机支持更多语言,通常使用 0x00~0x7f 范围的1个字节来表示 1个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码 [1]
0x00~0x7f
0x80~0xFFFF
ANSI转义序列是一种带内信号的转义序列标准, 相关视频终端控制光标屏幕位置、文本显示、显示样式等操作。[2]
注意,需要在Linux/MacOS 的环境下学习和使用
echo -e "\x1b[31m helloworld\x1b[0m"
\x1b[31m 后面的文本字体色为红色,其中31m就是代表ANSI中的红色
\x1b[31m
31m
ANSI
\x1b[0m 关闭所有属性
\x1b[0m
前景色范围在 40 - 49
40 - 49
echo -e "\x1b[41m helloworld\x1b[0m"
\x1b[41m 后面的后景色为红色,其中41m就是代表ANSI中的红色
\x1b[41m
41m
文本颜色范围在 40 - 49
echo -e "\x1b[5m helloworld\x1b[0m"
\x1b[5m
const process = require("process"); const frame = "▊"; /** * 等待操作 * @param {number} time */ async function sleep(time = 10) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time) }) } /** * 换行操作 */ function printNewLine() { process.stdout.write(`\x1b[0C \x1b[K\r\n`); } class Progress { async run(time = 1000, percent = 100, modulo = 2) { const count = Math.floor(percent / modulo); for (let i = 0; i < count; i ++) { await sleep(time / count); this._print(); } printNewLine(); } _print() { // 控制打印进度条每帧的内容 // 重复使用的时候打印开始会从上次结束的位置开始 process.stdout.write(`\x1b[K${frame}`); } } const progress = new Progress(); progress.run(1000, 100);
const process = require("process"); /** * 等待操作 * @param {number} time */ async function sleep(time = 10) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time) }) } /** * 换行操作 */ function printNewLine() { process.stdout.write(`\x1b[0C \x1b[K\r\n`); } /** * 清行操作 * @param {number} len 原有文本长度 */ function clearLine(len) { process.stdout.write(`\x1b[${len}D`); } const frame = "▓"; const backgroundFrame = "░"; class Progress { async run(time = 1000, percent = 100, modulo = 2) { const count = Math.floor(percent / modulo); for (let i = 0; i < count; i ++) { await sleep(time / count); const progressLength = this._printProcess(i, count, modulo); if (i < count - 1) { clearLine(progressLength); } } printNewLine(); } /** * 打印进度条 * @param {number} index 进度条当前帧索引位置 * @param {number} count 进度条帧数 * @param {number} modulo 进度条帧率变化模数 */ _printProcess(index, count, modulo) { let progressLength = 0; for (let i = 0; i < count; i ++) { if (i <= index) { progressLength += this._print(frame); } else { progressLength += this._print(backgroundFrame); } } let percentNum = (index + 1) * modulo; percentNum = Math.min(100, percentNum); percentNum = Math.max(0, percentNum); progressLength += this._print(` ${percentNum}%`); return progressLength; } /** * 打印终端文本 * @param {string} text 文本 * @param {number} leftMoveCols 光标左移动位数 */ _print(text, leftMoveCols) { let code = `\x1b[K${text}`; if (leftMoveCols >= 0) { code = `\x1b[${leftMoveCols}D\x1b[K${text}`; } process.stdout.write(code); return code.length; } } const progress = new Progress(); progress.run(1000, 100);
const process = require("process"); /** * 等待操作 * @param {number} time */ async function sleep(time = 10) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time) }) } /** * 换行操作 */ function printNewLine() { process.stdout.write(`\x1b[0C \x1b[K\r\n`); } class Loading { constructor() { this._intervalId = -1; this._beforeLength = 0; this._isDuringLoading = false; this._loadingIndex = 0; } /** * Loading动画开始操作 * @param {number} speed Loading每帧速度 */ start(speed = 100) { if (this._isDuringLoading === true) { return; } this._intervalId = setInterval(() => { this._printLoadingText(); }, speed); this._isDuringLoading = true; } /** * Loading 停止操作 */ stop() { clearInterval(this._intervalId); printNewLine(); this._isDuringLoading = false; this._loadingIndex = 0; } /** * 打印 Loading 操作 */ _printLoadingText() { const max = 10; if (this._loadingIndex >= max) { this._loadingIndex = 0; } const charList = []; for (let i = 0; i < max; i++) { if (i === this._loadingIndex) { charList.push('==>'); } else { charList.push(' '); } } const loadingText = [...['['], ...charList, ...[']']].join(''); this._print(loadingText); this._loadingIndex += 1; } _print(text) { const code = `\x1b[${this._beforeLength}D \x1b[K ${text}` process.stdout.write(code); this._beforeLength = code.length; } } async function main() { const loading = new Loading(); loading.start(); await sleep(2000); loading.stop(); } main();
[1] 百度百科: ANSI [2] 维基百科: ANSI转义序列
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
经常用系统终端进行技术开发的过程会看到一些终端的动画效果,例如这个
最近刚好好奇的了解了一下,才知道实现这个能力是利用了终端支持的
ANSI转义序列
,任何语言只要能调用终端的标准输入/输出(stdin/stdout
),都可以直接使用ANSI转义序列
的规范制作对应的终端动画。什么是ANSI
ANSI是一种字符代码,为使计算机支持更多语言,通常使用
0x00~0x7f
范围的1个字节来表示 1个英文字符。超出此范围的使用0x80~0xFFFF
来编码,即扩展的ASCII编码 [1]什么是ANSI转义序列
ANSI转义序列是一种带内信号的转义序列标准, 相关视频终端控制光标屏幕位置、文本显示、显示样式等操作。[2]
历史 [2]
规范历史
系统支持历史
基本使用
输出带前景色的文本
\x1b[31m
后面的文本字体色为红色,其中31m
就是代表ANSI
中的红色\x1b[0m
关闭所有属性前景色范围在
40 - 49
输出带后景色的文本
\x1b[41m
后面的后景色为红色,其中41m
就是代表ANSI
中的红色\x1b[0m
关闭所有属性文本颜色范围在
40 - 49
输出闪烁的文本
\x1b[5m
后面的文本闪烁\x1b[0m
关闭所有属性广泛支持的 ANSI 转义序列
Node.js例子
例子一: 实现一个简单的控制台进度条
实现效果
实现源码
例子二: 实现一个带背景和数字变化的进度条
实现效果
实现源码
例子三: 实现一个等待Loading效果
实现效果
实现源码
参考资料
[1] 百度百科: ANSI
[2] 维基百科: ANSI转义序列
The text was updated successfully, but these errors were encountered: