控制台居然可以这么玩?五分钟带你上手ANSI指令,实现一个log工具包

embedded/2024/9/22 15:35:25/

目录

前言

基础知识

进阶实践

ANSI参数

ANSI类

JSLog类

工具的使用说明

配置相关

全局配置项

默认配置

基本用法

打印字符

添加全局配置项

清空所有样式及操作行为

校验传入的参数是否正确

样式控制

Node环境

浏览器中

光标控制指令

光标位置偏移

滚动条控制

其他用法

log函数的链式调用

同一行打印多种log

属性的指令集

ANSI相关的操作

ANSI编码指令拼接

颜色设置

字体样式

光标操作

光标移动

滚动条移动

ANSI颜色指令的进阶用法

兼容模式

真彩模式

光标坐标轴移动

写在最后

相关代码:


前言

在日志系统或者工程化打包插件中我们时常会看到控制台输出五颜六色的字体以及符号,这些字体大多是使用ANSI转义序列来实现的,ANSI转义序列(或ANSI转义码)是一种用于控制文本输出格式的标准化方法,通常用于终端和控制台应用程序的样式或者输出,比如:粗体、斜体、颜色、背景,光标控制等,本篇文章将通过JS工具库的形式与大家分享常用的ANSI指令及进阶用法

基础知识

先来了解一下ANSI的基础用法,以node控制台为例,当我们使用console.log或print输出字符串时,在字符串中插入一些特殊字符,比如:

const logAndReset = (...args: string[]) => console.log(...args.map(it => it + `\x1b[0m`));
logAndReset(`普通字体`, `\x1b[1m加粗`, `\x1b[3m斜体`, `\x1b[36m青色`, `\x1b[42m绿色背景`);

效果如下: 

上文中的`\x1b[变量m`就是通过ANSI转义码的形式输出对应指令,这些序列以Escape字符(ASCII码27,也就是Esc)开头,后面跟着一些特定的字符,用于控制终端的颜色、样式和其他属性。其中x1b代表16进制的17,此外,在node中我们还可以使用Unicode中的\u001b表示:`\u001b[1m`。m表示设置文本样式的参数,除了m外,还可以通过其他参数控制其他功能:A表示光标向上移动;2J表示清除整个屏幕等等。

更多参数指令使用方式可以参考这个文件:src/static.ts · 阿宇的编程之旅/js-log-lib - Gitee.com

进阶实践

有了上面的概念,我们可以尝试使用代码实现一个工具,将ANSI语义化,使用参数控制其样式和行为

ANSI参数

首先是收集基础的指令的文件static,这里面存放的是需要用到的ANSI参数


/**
0:重置所有样式
1:粗体(高亮)
2:暗淡(降低亮度)
3:斜体
4:下划线
5:闪烁
7:反显(交换前景色和背景色)
8:隐藏(不可见)
9:划掉(删除线)
21:关闭粗体(粗体关闭)
22:正常颜色和粗体(关闭粗体和暗淡)
23:关闭斜体
24:关闭下划线
25:关闭闪烁
27:关闭反显
28:关闭隐藏
29:关闭删除线
30 到 37:设置前景色(文本颜色)
38:设置前景色为RGB颜色
39:重置前景色为默认颜色
40 到 47:设置背景色
48:设置背景色为RGB颜色
49:重置背景色为默认颜色
90 到 97:设置高亮前景色
100 到 107:设置高亮背景色
**/export const ANSIParams = {// 2:真彩色模式; 5:兼容模式colorMode: {trueColor: "2",compatibility: "5",},// 48:背景颜色,38:字体颜色colorType: {foreground: "38",background: "48"},color: {black: 30,red: 31,green: 32,yellow: 33,blue: 34,magenta: 35,cyan: 36,white: 37,bgBlack: 40,bgRed: 41,bgGreen: 42,bgYellow: 43,bgBlue: 44,bgMagenta: 45,bgCyan: 46,bgWhite: 47,brightBlack: 90,brightRed: 91,brightGreen: 92,brightYellow: 93,brightBlue: 94,brightMagenta: 95,brightCyan: 96,brightWhite: 97,bgBrightBlack: 100,bgBrightRed: 101,bgBrightGreen: 102,bgBrightYellow: 103,bgBrightBlue: 104,bgBrightMagenta: 105,bgBrightCyan: 106,bgBrightWhite: 107,},textStyle: {reset: 0,// 重置所有样式bold: 1,// 粗体dim: 2,// 暗淡(降低亮度)italic: 3,// 斜体underline: 4,// 下划线inverse: 7,// 闪烁hidden: 8,// 隐藏strikethrough: 9,// 删除线noBold: 21,    // 关闭粗体noItalic: 23,  // 关闭斜体noUnderline: 24,  // 关闭下划线noBlink: 25,   // 关闭闪烁noInverse: 27, // 关闭反显noHidden: 28,  // 关闭隐藏noStrikethrough: 29,  // 关闭删除线},cursor: {savePosition: `s`, // 保存光标位置restorePosition: `u`, // 恢复光标位置reportPosition: `6n`, // 获取光标位置toStart: `H`, // 将光标移动到屏幕的左上角toLineStart: `E`, // 将光标移动到当前行的开头toLineEnd: `F`, // 将光标移动到当前行的末尾eraseDisplay: `2J`, // 清除整个屏幕eraseLine: `K`, // 清除从光标位置到行尾的内容direct: {custom: `H`,// 光标移动到指定行列up: `A`,// 光标向上移动down: `B`,// 光标向下移动right: `C`,// 光标向右移动left: `D`,// 光标向左移动}},scroll: {up: "S",down: "T"}
}

通过这些参数也可以直接使用console.log的形式打印出效果,可以看到,我将指令分成了六类:

colorMode:显示灰度色或RGB颜色,前者兼容性更好,后者颜色更丰富(具体用法在后文会讲到)
colorType:当使用了调色板时,设置前景色还是背景色
color:默认的颜色指令
textStyle:字体样式
cursor:光标控制相关的
scroll:滚动条相关

为了方便使用,我使用下面的ANSI类对上述指令做了规范化处理

ANSI类

围绕上面的基础指令,我们可以将对应的参数传入函数中达到效果

// ANSI 类,用于生成 ANSI 控制码
class ANSI {// moveTo 用于生成移动光标的 ANSI 控制码moveTo = (opts: IMoveParams) => {const { direct, position } = opts ?? {}if (!direct || !position) return ``let _p = ``if (direct === "custom" && typeof position === "object") {const { col, row } = position_p = `${row};${col}`} else if (typeof position !== "object") {_p = `${position}`}if (_p) return `${_p}${ANSIParams.cursor.direct[direct] ?? ""}`return ``}// scrollTo 方法,用于生成滚动屏幕的 ANSI 控制码scrollTo = (row: number) => {if (!row) return ``// 滚动条向下滚动,代码对应向上移动一行,相当于删除(backspace)row行let direct = ANSIParams.scroll.downif (row < 0) {// 滚动条向上滚动,代码对应向下移动一行,相当于回车(enter)row行direct = ANSIParams.scroll.up}return `${Math.abs(row)}${direct}`}// 生成光标相关的 ANSI 控制码getCursor = (cursor: ICursor) => {if (cursor) return `${ANSIParams.cursor[cursor] ?? ""}`else return ``}// 生成颜色和样式相关的 ANSI 控制码getColorMode = (mode: IColorMode) => `${ANSIParams.colorMode[mode] ?? "2"}`getColorType = (type: IColorType) => `${ANSIParams.colorType[type] ?? "38"}`getRGB = (options: Partial<IRGB>) => {const { type = "foreground", mode = "trueColor", red = 0, green = 0, blue = 0, color = 0 } = optionsconst cType = this.getColorType(type), cMode = this.getColorMode(mode)if (cMode === ANSIParams.colorMode.compatibility) return `${cType};${cMode};${color}`return `${cType};${cMode};${red};${green};${blue}`}getColor = (color: IColorStr) => {if (color) return `${ANSIParams.color[color] ?? ""}`else return ``}getTextStyle = (textStyle: ITextStyle) => {if (textStyle) return `${ANSIParams.textStyle[textStyle] ?? ""}`else return ``}// 生成最终的 ANSI 控制码字符getANSI = (params: string, isStyle: boolean = true) => params ? `\x1B[${params}${isStyle ? "m" : ""}` : ""// 重置所有样式reset = () => this.getANSI(`0`)
}

其中TS类型代码如下:


import { ANSIParams } from "./static.js"
// 颜色字符串的索引
export type IColorStr = keyof typeof ANSIParams.color
// 前(后)景色可以是 RGB 对象或颜色字符串
export type IColor = IRGB | IColorStr
// 文本样式
export type ITextStyle = keyof typeof ANSIParams.textStyle
// 光标的方向
export type IDirect = keyof typeof ANSIParams.cursor.direct
// 光标操作的索引
export type ICursor = keyof Omit<typeof ANSIParams.cursor, "direct">
export type IGlobalOpts = Partial<{text: string; // 要打印的文本reset: boolean; // 是否在末尾重置样式type: string; // console的类型split: boolean; // 是否拆分显示,在node中可以在一个console.log中分开显示,比如:console.log("1","2","3")和console.log("123"),前者在字符之间会有空格,split为true表示使用前者显示,反之使用后者,在浏览器环境下只能合并显示color: IColor[] | IColor; // 前(后)景色或其数组,方便传入多个颜色参数,但是有些控制台似乎不兼容textStyle: ITextStyle[] | ITextStyle; // 文本样式或样式数组cursor: ICursor; // 光标控制move: IMoveParams; // 光标移动参数scroll: number; // 滚动条行数style: Partial<CSSStyleDeclaration>; // CSS样式对象,仅支持浏览器环境
}>
export type IOpts = Omit<IGlobalOpts, "type" | "split">
// 颜色模式,2:真彩色模式; 5:兼容模式; null:默认颜色模式
export type IColorMode = keyof typeof ANSIParams.colorMode
// 颜色类型,48:背景颜色,38:字体颜色
export type IColorType = keyof typeof ANSIParams.colorType
export type IRGB = {red?: string | numbergreen?: string | numberblue?: string | numbertype?: IColorType // 字体颜色或背景颜色mode?: IColorModecolor?: string | number// 兼容模式下,256色调色板的颜色索引
}
export type IPosition = {col: number | string // 列row: number | string // 行
}
export type IMoveParams = {direct: IDirect// 移动方向position: IPosition | number | string// 移动到的位置,可以是坐标对象或数字
}

我们可以直接使用上面的类配合log输出样式到控制台

const ansi = new ANSI();
const { getANSI, getTextStyle, reset } = ansi;
console.log(getANSI(getTextStyle("bold")), "加粗", reset());

实际上只使用ANSI操作控制台,有一个ANSI类就够了,但是我在工具中使用了JSLog对ANSI进行了拓展

JSLog类

实现JSLog类的目的有以下几点

  • 日志格式化:根据传入参数的不同,使用不同的ANSI控制码和样式信息
  • 环境适配:通过判断是在Node环境还是浏览器环境,做出相应的处理
  • 全局选项配置:日志默认使用全局的配置,每个日志函数可以通过传入配置达到封装目的
  • 可读性和维护性:相对于ANSI类,更易读,方便拓展并提升维护性

下面是JSLog的代码

const defaultOpts: IGlobalOpts = {reset: true,type: "log",split: true
}
// JSLog 类,继承 ANSI 类,用于生成日志,并根据环境打印到控制台或执行其他操作
export class JSLog extends ANSI {readonly stylePlaceholder = "%c"; // 浏览器环境下字符串占位符:%cisNode: boolean = typeof process !== "undefined";private globalOpts: IGlobalOpts = defaultOpts// 全局选项作为参数constructor(globalOpts?: IGlobalOpts) {super()this.checkOptions(globalOpts)this.mixins(globalOpts)}// 合并全局选项mixins(opts: IGlobalOpts) {this.globalOpts = emptyObject({ ...this.globalOpts, ...opts })return this.globalOpts}// 清空全局样式及行为clear() {this.globalOpts = emptyObject(defaultOpts)}// 生成日志log = (...args: IOpts[]) => {if (args.length <= 0) return thisconst { logQueue, styleQueue } = this.createQueue(args)this.logFn(logQueue, styleQueue)return this}// 生成日志队列private createQueue = (args: IOpts[]) => {const styleQueue = [], logQueue: string[] = []const { isNode, stylePlaceholder, globalOpts } = thisargs.forEach(it => {this.checkOptions(it)const { text = "" } = itconst _opts = emptyObject({ ...globalOpts, ...it })const { reset, style = {} } = _optsconst hasStyle = !isEmptyObject(style)logQueue.push(`${this.formate(_opts)}${hasStyle ? stylePlaceholder : ""}${text}${reset ? this.reset() : ""}`)if (hasStyle) {const _style = this.formatStyle(style)_style && styleQueue.push(_style)}})if (isNode && globalOpts.split) {return { logQueue, styleQueue }}return { logQueue: logQueue.join(""), styleQueue }}// 格式化选项生成 ANSI 控制码private formate(opts: IOpts) {const { color, textStyle, cursor, move, scroll } = opts// TODO:控制渲染执行顺序const params = this.getANSI(`${this.formatCursor(cursor)}${this.moveTo(move)}${this.scrollTo(scroll)}`, false) + this.getANSI(`${this.formatColor(color)}${this.formatText(textStyle)}`)return params}// formatParams、formatText、formatColor方法,格式化样式和颜色选项private formatCursor(cursor: ICursor) {if (typeof cursor === "string") {return this.getCursor(cursor)}return ``}private formatText(textStyle: ITextStyle[] | ITextStyle) {if (typeof textStyle === "string") {return this.getTextStyle(textStyle)} else if (typeof textStyle === "object") {let __temp = ``;textStyle.forEach(it => __temp += this.getTextStyle(it))return __temp}return ``}private formatColor(color: IColor[] | IColor) {if (typeof color === "string") {return this.getColor(color)} else if (typeof color === "object") {if (getType(color) === "array") {let __temp = ``;(color as IColor[]).forEach(it => __temp += this.formatColor(it))return __temp} else {return this.getRGB(color as IRGB)}}return ``}// 格式化浏览器端的style样式属性private formatStyle(styles: Partial<CSSStyleDeclaration>) {let styleStr = ``Reflect.ownKeys(styles).forEach(k => {if (typeof k === "string") styleStr += `${toKebabCase(k)}:${styles[k]};`})return styleStr}// 执行打印操作private logFn(logQueue: string[] | string, styleQueue: string[]) {const { globalOpts } = thisconst { type } = globalOptsif (typeof logQueue === "string") {return console[type](logQueue, ...styleQueue)}return console[type](...logQueue, ...styleQueue)}// 校验options参数checkOptions(opts: IGlobalOpts = {}) {const { isNode } = thisconst { cursor, move, scroll, style, color, textStyle } = optsif (isNode && style) throw Error("node环境下无法使用style相关属性")if (!isNode && (cursor || move || scroll)) throw Error("window环境下无法使用光标相关属性")if (!isNode && style && (color || textStyle)) console.warn("请注意:样式可能会冲突")}
}

工具实现完毕,来看看下面的功能介绍 

工具的使用说明

工具说明同样可以参照:README.md · 阿宇的编程之旅/js-log-lib - Gitee.com

配置相关

全局配置项

参照 types.ts 中的 IGlobalOpts 类型,全局配置项可以传入以下属性

type IGlobalOpts = Partial<{text: string; // 要打印的文本reset: boolean; // 是否在末尾重置样式type: string; // console的类型split: boolean; // 是否拆分显示,在node中可以在一个console.log中分开显示,比如:console.log("1","2","3")和console.log("123"),前者在字符之间会有空格,split为true表示使用前者显示,反之使用后者,在浏览器环境下只能合并显示color: IColor[] | IColor; // 前(后)景色或其数组,方便传入多个颜色参数,但是有些控制台似乎不兼容textStyle: ITextStyle[] | ITextStyle; // 文本样式或样式数组cursor: ICursor; // 光标控制move: IMoveParams; // 光标移动参数scroll: number; // 滚动条行数style: Partial<CSSStyleDeclaration>; // CSS样式对象,仅支持浏览器环境
}>;

默认配置

在index.ts中defaultOpts是默认配置,当开发者没有设置option时,会使用这里的样式和设置

基本用法

打印字符

const logger = new JSLog();
logger.log({ text: "阿宇的编程之旅" }); // 阿宇的编程之旅

添加全局配置项

全局配置的增加方式有两种,一是在实例化对象时传入构造函数,第二种是通过 mixins 添加

const logger = new JSLog({ type: "error", color: "red" });
logger.log({ text: "我是个错误提示" });
logger.mixins({ type: "info", color: "green" });
logger.log({ text: "我是个成功提示" });

清空所有样式及操作行为

const logger = new JSLog({ type: "error", color: "red" });
logger.log({ text: "阿宇" });
logger.clear();
logger.log({ text: "阿宇" });

校验传入的参数是否正确

const logger = new JSLog();
// node中
logger.checkOptions({ style: {} }); // node环境下无法使用style相关属性

样式控制

Node环境

在node环境下使用color控制文字的前(后)景色,使用textStyle控制文字形态样式,如加粗,斜体等(不同的控制台对样式兼容性不相同,可能会导致冲突或失效问题)

const logger = new JSLog();
logger.log({text: "hello world",color: "cyan", // 字体青色textStyle: "bold", // 加粗
});

 

浏览器中

浏览器环境下也可以使用color,textStyle控制文字样式,但是使用style属性可以支持更多的样式设置,具体可以参考CSSStyleDeclaration类型,如果style与上述的color,textStyle同时设置了,可能会导致样式冲突

logger.log({text: "hello world",style: { color: "lightblue", background: "#333", margin: "10px" },
});

光标控制指令

光标控制只支持在Node环境下使用,和样式调整类似,其原理也是使用ANSI编码在控制台输出对应指令来完成的

const logger = new JSLog();
logger.log({ text: "i m here" });
logger.log({ text: "i m here" });
logger.log({ text: "i m here", cursor: "eraseDisplay" }); // 清除屏幕

光标位置偏移

光标移动与上述的光标指令相同,只不过在指令中传入了移动距离

const logger = new JSLog();
logger.log({move: {direct: "right",position: 5,},text: "编程之旅", // 先打印后面的字符},{move: {direct: "left",position: 14,},text: "阿宇的", // 将前面的字符插入到左边}
);

滚动条控制

借助之前写的TimerManager定时器,或者直接使用setinterval实现一个向上滑动滚动条的效果,每500毫秒打印一次行数

import { TimerManager } from "utils-lib-js";
const logger = new JSLog();
const timerManager = new TimerManager();
let scroll = 0;
const timer = timerManager.add(() => {if (scroll <= -5) return timerManager.delete(timer);scroll--;logger.log({ text: scroll.toString(), scroll });
}, 500);

其他用法

除了上述核心用法之外,JSLog还支持下面的进阶用法

log函数的链式调用

由于log函数返回了当前类,这就使开发者可以直接调用本身的其他函数进行操作

const logger = new JSLog();
logger.log({ text: "hello" }).log({ text: "world" });

同一行打印多种log

const logger = new JSLog({ split: false });
logger.log({ text: "阿宇", color: "red" },{ text: "的编程", color: "brightCyan" },{ text: "之旅", color: "bgBrightMagenta" }
);

属性的指令集

上面我们提到了options可以传入一种指令控制样式或者行为,某些属性支持传入一个数组,批量设置属性

logger.log({text: "hello world",color: ["red", "bgBrightGreen"], // 红色字体,亮绿背景textStyle: ["bold", "italic", "strikethrough"], // 加粗,斜体,删除线
});

ANSI相关的操作

上面我们说到JSLog类继承于ANSI类,所以一些ANSI指令操作在JSLog中也是支持的,下面就举例说说常用的指令函数

ANSI编码指令拼接

通过getANSI和ANSI编码达到控制样式的效果,使用console或者直接使用JSLog直接输出getANSI的转移字符可以设置对应的指令

const logger = new JSLog();
const { getANSI } = logger;
console.log(getANSI(`35`), "hello world"); // 输出紫色的字符串
logger.log({text: getANSI(`34`) + "hello world",
}); // 输出蓝色的字符串

 

颜色设置

使用下面的getColor函数可以设置对应的颜色样式

const logger = new JSLog();
const { getANSI, getColor } = logger;
logger.log({text: getANSI(getColor("bgBlue")) + "hello world",
}); // 输出蓝色背景的字符串

字体样式

使用getTextStyle函数可以设置对应的字体样式

const logger = new JSLog();
const { getANSI, getTextStyle } = logger;
logger.log({text: getANSI(getTextStyle("bold")) + "hello world",
}); // 输出加粗效果的字符串

光标操作

如果你想直接操作光标,不妨直接使用getCursor函数

const logger = new JSLog();
const { getANSI, getCursor } = logger;
logger.log({ text: "----------------------------" },{text: getANSI(getCursor("toLineStart"), false) + "hello world",}
); //移动到行首并输出字符

光标移动

使用moveTo函数可以直接移动光标

const logger = new JSLog();
const { getANSI, moveTo } = logger;
logger.log({ text: "hello" },{text: getANSI(moveTo({ direct: "right", position: 5 }), false) + "world",}
); //向右移动5次光标

滚动条移动

使用scrollTo函数可以移动滚动条

const logger = new JSLog();
const { getANSI, scrollTo } = logger;
logger.log({text: getANSI(scrollTo(-2), false) + "hello world",
}); //向上移动2行滚动条

重置样式

通过运行reset函数可以重置样式,由于jslog默认配置了reset参数,所以样式只会在一个log队列中生效,要取消重置可以在实例化时传入reset: false,这里我直接使用console.log展示效果

const logger = new JSLog();
const { getANSI, getColor, reset } = logger;
console.log(getANSI(getColor("bgBrightGreen")),"i m bgBrightGreen",reset(),"i m reset"
);

ANSI颜色指令的进阶用法

上面说到了前后景色的设置,使用color属性可以对log的字符设置对应的颜色属性,然而由于颜色的指令数有限,有许多其他颜色无法表示,所以ANSI提供了两种设置颜色参数的方式

兼容模式

第一种是256种色调色板的形式,又叫做兼容模式,设置该模式需要将ANSI的第二个参数 mode 设置为5:

const ANSI = `\x1B[38;5;<color>m`;

它支持设置数字0-255:

0-15是标准颜色,也就是之前用到的颜色变量

16-231有216种颜色,这些颜色按照六阶的RGB立方体规则排列,提供了各种颜色的组合

232-255是灰度颜色,这些颜色表示不同灰度级别,从黑到白。232表示最暗的灰色,255表示最亮的灰色。

通过下面的代码我们可以打印出全部256种色阶:

const logger = new JSLog();
const { getANSI, getRGB, reset } = logger;
let color = ``;
for (let i = 0; i < 255; i++) {color += `${getANSI(getRGB({ color: i, mode: "compatibility", type: "background" }))}  `;
}
console.log(color, reset());

 

真彩模式

另一种设置颜色的方式是使用TrueColor(真彩色)模式。在TrueColor模式下,可以通过指定RGB(红绿蓝)值来准确设置颜色,而不仅仅依赖于预定义的颜色索引,就像是css中的rgb方法。该模式需要将ANSI的第二个参数mode设置为2:

const ANSI = `\x1B[38;2;<r>;<g>;<b>m`;

它支持设置数字 256^3 种(16777216 种颜色),在真彩模式下可以通过指定每个颜色通道的具体强度值来准确定义颜色

const logger = new JSLog();
logger.log({ text: "hello", color: { red: 255 } },{text: "阿宇的",color: { green: 255 },},{ text: "编程之旅", color: { blue: 255, type: "background" } }
);

下面是一个使用rgb实现的简易调色板,这里我将步长设置为26

const logger = new JSLog();
const { getANSI, getRGB, reset } = logger;
let color = ``
for (let r = 0; r <= 255; r += 26) {for (let g = 0; g <= 255; g += 26) {for (let b = 0; b <= 255; b += 26) {color += `${getANSI(getRGB({ red: r, green: g, blue: b, mode: "trueColor", type: "background" }))} ${reset()}`}}
}
console.log(color);

光标坐标轴移动

通过配置move参数我们还可以实现指针坐标位移的功能

下面的代码中我们实现了一个斜着输出hello world字符串的功能

import { TimerManager } from "utils-lib-js";
const logger = new JSLog();
const timer = new TimerManager(); // 创建定时器
const str = "hello world";
const position = {row: 0, // 行col: 0, // 列
};
logger.log({ cursor: "eraseDisplay" }); // 清屏
timer.add(() => {const _str = str[position.col];if (!_str) timer.clear();position.col++;position.row++;move(_str);
}, 100);const move = (str) =>logger.log({text: str,move: {direct: "custom",position,},});

写在最后

本篇文章通过ANSI控制码入手,由浅及深的介绍了其用法,在控制台中我们可以使用ANSI来设置字符样式,光标移动等等,接着我实现了一个浏览器和服务端共用的JS工具库,帮助大家更好的操控log以及理解ANSI操作样式的原理。

以上就是文章的全部内容了,感谢你看到了最后,如果觉得文章不错的话,还望三连支持一下!谢谢~

相关代码:

日志工具:js-log-lib: 浏览器和node共用的log样式调整工具,可以自定义输出样式

文中的定时器:https://gitee.com/DieHunter/timer-manager-lib

工具包:utils-lib-js: JavaScript工具函数,封装的一些常用的js函数

以上代码均可以使用(p)npm下载,期待你的建议

主仓库:myCode: 基于js的一些小案例或者项目


http://www.ppmy.cn/embedded/11735.html

相关文章

CentOS系统上经常使用的一些基本命令

CentOS是一种流行的Linux发行版&#xff0c;常见的命令包括&#xff1a; 文件和目录操作&#xff1a; ls: 列出目录内容。cd: 切换目录。pwd: 显示当前工作目录。mkdir: 创建新目录。rm: 删除文件或目录。cp: 复制文件或目录。mv: 移动文件或目录。 文件查看和编辑&#xff1a…

QT中使用QTableView控件

1.与数据库连接&#xff0c;读取数据库内容到UI界面显示 // 连接SQLite数据库db QSqlDatabase::addDatabase("QSQLITE","second");db.setDatabaseName("./testitem.db"); // 替换为你的数据库文件路径if (!db.open()) {qDebug() << &quo…

stm32实现hid鼠标

启动CubelMX 选择芯片&#xff08;直接输入stm32f103zet6) 设置时钟 如下图 usb设置 配置usb设备 调试端口设置 配置时钟 项目输出设置 打开工程&#xff08;后记&#xff1a;此工程含有中文不能编译通过) 配置项目 配置调试器 编译无法通过 删除路径中的中文&#xff0c;以及…

前端科举面经-HTML篇

前端面试-HTML篇 什么是http?http和https有什么区别https的加密过程?http2.0有什么改进?src和href的区别对html语义化标签的理解?script标签中defer和asyc的区别?举出几个常见的行内、块级元素什么是webworker&#xff1f;iframe的优缺点&#xff1f;介绍一下tcp三次握手f…

vue项目使用百度地图

打开百度地图开放平台 百度地图开放平台 | 百度地图API SDK | 地图开发 在控制台新建应用 复制访问应用的ak 可修改地图样式 使用部分 <!-- 引入地图 --><div class"main-aside"><div id"b-map-container"></div></div> …

电机控制专题(一)——最大转矩电流比MTPA控制

文章目录 电机控制专题(一)——最大转矩电流比MTPA控制前言理论推导仿真验证轻载1Nm重载30Nm 总结 电机控制专题(一)——最大转矩电流比MTPA控制 前言 MTPA全称为Max Torque Per Ampere&#xff0c;从字面意思就可以知道MTPA算法的目的是一个寻优最值问题&#xff0c;可以从以…

Vue--》深入了解 VueUse 功能性工具集

今天博主为大家介绍一款实用性的插件名字叫做 VueUse &#xff0c;它是专门为 Vue.js 生态系统设计的功能性工具集合。其提供了许多可重用的功能函数&#xff0c;可以帮助开发者更轻松地构建 Vue.js 应用程序。其提供了大量的功能&#xff0c;包括状态管理、副作用管理、组合式…

Vue3:响应式数据的基本使用(ref、reactive)

一、前言 在Vue3中&#xff0c;如果数据不是响应式数据&#xff0c;当数据的值发生改变时&#xff0c;页面上的数据是不会发生改变的。因此本文主要介绍Vue3中响应式数据的使用&#xff0c;包括ref和reactive的基本使用。 二、ref 1、ref —— 创建基本类型的响应式数据 re…