ASCII Art 与 FIGlet 字体:从终端横幅到纯原生 TypeScript 实现

一、ASCII Art:从电传打字机到现代终端

ASCII Art(ASCII 艺术)是用可打印 ASCII 字符拼合成图案的视觉艺术形式。它的历史比大多数人想象的更久远:

  • 1966 年:Ken Knowton 在贝尔实验室用字符打孔卡片生成图案,被视为 ASCII Art 的先驱
  • 1970 年代:电传打字机(TTY)时代,开发者用字符绘制流程图、电路图、Logo
  • 1991 年:Glenn Chappell 与 Ian Chai 发布 FIGlet,让 ASCII Art 文本横幅生成进入自动化时代
  • 现代:GitHub README 标题、CLI 启动横幅、源码注释装饰、终端签名、Slack/IRC 表情

即使在图形化界面普及的今天,ASCII Art 仍有不可替代的价值:

  1. 纯文本兼容:任何环境(终端、邮件、Markdown、代码注释)都能正确显示,无需图片
  2. 等宽即对齐:在等宽字体下,字符天然对齐,无需额外排版
  3. 零依赖传播:复制粘贴即用,无需下载字体文件或图片资源
  4. 版本控制友好:作为文本存储在 Git 中,diff 清晰可读

二、FIGlet 与 FIGfont 字体格式

2.1 FIGlet 程序

FIGlet 是最经典的 ASCII Art 字体渲染程序。它接收文本输入,按指定字体渲染为多行 ASCII Art 输出。核心特性:

  • 字体可插拔:字体独立存储为 .flf 文件,程序与字体解耦
  • 多渲染模式:支持全宽(full width)、紧排(kerning)、压缩(smushed)三种字符拼接模式
  • 社区生态:FIGfont 仓库收录了数百种字体,如 Standard、Big、Slant、Shadow、Banner 等

2.2 FIGfont 文件格式

每个 .flf 文件由头部与字符数据两部分组成:

flf2a$ 7 5 16 15 11 0 24321
   ↑    ↑ ↑  ↑  ↑  ↑ ↑   ↑
   │    │ │  │  │  │ │   └─ 注释行数
   │    │ │  │  │  │ └─── 旧布局(已废弃)
   │    │ │  │  │  └───── 打印方向(0=左到右,1=右到左)
   │    │ │  │  └──────── 硬空白列数
   │    │ │  └─────────── 字符最大宽度(列数)
   │    │ └────────────── 基线行号(从 1 开始)
   │    └───────────────── 字符高度(行数)
   └────────────────────── 签名行 + 硬空白字符(此处为 $)

字符数据按 ASCII 码顺序排列,每个字符占 height 行,行间用 @ 等结束符分隔。硬空白字符(如 $)表示字符内的空格,避免被行尾 trim 去除。

2.3 拼接模式

FIGlet 支持三种字符拼接模式:

模式说明效果
全宽每字符占满最大宽度,字符间无重叠视觉最宽,对齐最整齐
紧排相邻字符的空白列重叠视觉紧凑,部分字符可粘连
压缩相邻字符的边界字符按规则合并视觉最紧凑,字形可能变形

本工具采用「全宽 + 可调间距」策略:字符间用 0-2 个空格分隔,既保证可读性,又提供灵活性。相比 FIGlet 的复杂紧排算法,这种方案实现简单且效果可控。

三、三种字体的设计差异

本工具内置 Block、Banner、Small 三种字体,分别对应不同使用场景:

3.1 Block(5 行高,方块风格)

 █████
██   ██
███████
██   ██
██   ██
  • 字符宽度:统一 7 列,视觉最整齐
  • 填充字符(全角块),对比度最高
  • 适用场景:终端标题、README 主标题、文档醒目位置
  • 设计取舍:7 列宽度让 M、W 等宽字符也能紧凑显示,但 I 等窄字符需居中填充

3.2 Banner(7 行高,宽幅风格)

   ██
  ████
 ██  ██
████████
██    ██
██    ██
██    ██
  • 字符宽度:8 列,视觉最宽
  • 填充字符,但字形更圆润(如 A 的斜边)
  • 适用场景:启动横幅、CLI 欢迎 Banner、大标题
  • 设计取舍:7 行高占用纵向空间多,但视觉冲击力最强

3.3 Small(3 行高,紧凑风格)

 ██
██ █
 ███
  • 字符宽度:变宽(2-8 列),最节省横向空间
  • 填充字符,但字形高度简化
  • 适用场景:多行输出、注释块标题、CLI 签名、邮件签名
  • 设计取舍:3 行高让多行内容不会过长,但字形辨识度略低

四、渲染算法核心

4.1 数据结构

每个字体定义为字符映射表:

type Font = {
  name: string;        // 字体名称
  height: number;      // 字体高度(行数)
  chars: Record<string, string>;  // 字符 -> 多行 ASCII Art(用 \n 分隔)
};

每个字符的 ASCII Art 用 \n 分隔的多行字符串表示:

'A': ' █████ \n██   ██\n███████\n██   ██\n██   ██'

4.2 横向拼接算法

渲染流程:

  1. 预处理:可选转大写(字体仅含大写字母)
  2. 逐字符查找:在字体表中查找字符数据,未找到用占位符 ? 渲染
  3. 行对齐:将每个字符的数据按 \n 切分为行数组,确保行数与字体高度一致
  4. 横向拼接:逐行将所有字符的对应行用空格分隔拼接
for (let row = 0; row < height; row++) {
  const rowParts = charBlocks.map(block => block[row] ?? '');
  const line = rowParts.join(separator);
  resultLines.push(line.trimEnd());
}

4.3 占位符处理

未覆盖字符(如中文、Emoji、Unicode 符号)统一用 ? 占位符渲染。占位符的尺寸与字体高度一致,保证输出不变形。实际使用中,建议将非 ASCII 字符替换为等价表达(如「工具」→「GONGJU」)。

五、应用场景实战

5.1 GitHub README 标题

███████ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ███████

# MyProject

一个简洁高效的项目描述...

用代码块包裹保持等宽对齐,GitHub 渲染器会保留 ASCII Art 的原始格式。

5.2 终端启动横幅

Node.js CLI 工具常在启动时输出 ASCII Art:

console.log(`
 ███████ ██   ██ ███████ ██
██         ██ ██  ██     ██
██          ███   ███████ ██
██         ██ ██  ██     ██
 ███████  ██  ██ ██     ███████
v1.0.0
`);

注意终端需使用等宽字体(Consolas、Monaco、Source Code Pro)才能正确对齐。

5.3 源码注释装饰

/*
 *  ███████ ██   ██ ███████ ██
 *  ██         ██ ██  ██     ██
 *  ██          ███   ███████ ██
 *  ██         ██ ██  ██     ██
 *  ███████  ██  ██ ██     ███████
 *
 *  模块入口:用户认证
 */

5.4 邮件签名

邮件签名中插入小型 ASCII Art Logo,既个性化又不会过于突兀:

  ██
 ██ █
  ███
GitHub: @yourname

六、纯原生 TypeScript 实现要点

6.1 零依赖设计

本工具不依赖 figlet npm 包或任何字体文件,而是:

  • 字体数据硬编码:3 种字体的字符数据直接写在 asciiArt.ts 中,约 200 行
  • 渲染算法纯 TS 实现:横向拼接 + 行对齐,无外部依赖
  • Bundle 体积可控:AsciiArtTool 组件 19KB(gzip 3.9KB),远低于 200KB 红线

6.2 字符集覆盖策略

字体覆盖 A-Z、0-9、空格与常用标点(共约 60 个字符)。选择依据:

  1. 使用频率:A-Z 与 0-9 覆盖 90% 以上的标题场景
  2. 标点选择! ? . , - _ : ; ' " ( ) / \ @ # $ % & * + = ~ ^ < > [ ] 覆盖常见标点
  3. 未覆盖字符:中文、Emoji、Unicode 符号用 ? 占位,避免渲染异常

6.3 等宽对齐保证

为保证输出对齐,每个字符的行数据需满足:

  • 行数一致:每字符的行数等于字体高度(Block 为 5 行)
  • 行宽一致(可选):Block 字体统一 7 列,Banner 统一 8 列;Small 字体允许变宽
  • 尾部 trim:渲染时去除每行尾部空白,避免行尾多余空格影响复制

6.4 复制与下载

  • 复制:复用共享 utils/clipboard.ts,优先 Clipboard API,降级 execCommand
  • 下载:用 Blob + URL.createObjectURL 生成临时下载链接,零依赖实现文件下载

七、字体设计注意事项

7.1 字符宽度统一 vs 变宽

  • 统一宽度:每字符占固定列数(如 Block 的 7 列),渲染最整齐,但 I 等窄字符需居中填充
  • 变宽:每字符按实际宽度,I 仅占 3 列,M 占 8 列,视觉更自然但需处理对齐

本工具 Block 与 Banner 采用统一宽度,Small 采用变宽,兼顾整齐与紧凑。

7.2 填充字符选择

常见填充字符:█▓▒░#*@|/\+=:-.。选择依据:

  • 对比度(全角块)对比度最高,适合标题
  • 可读性# 字符辨识度高,适合注释
  • 终端友好* + 等字符在所有终端都能正确显示

本工具统一用 填充,对比度最高。

7.3 字符间距作用

字符间距指相邻字符之间的空格列数:

  • 间距 0:字符紧挨,视觉最紧凑,但 H、I 等字符可能粘连
  • 间距 1(默认):字符间 1 列空格,可读性最佳
  • 间距 2:字符间距更宽,适合大字体或强调分隔

八、与现有工具的联动

ASCII Art 工具与工具盒子中的文本处理工具形成联动:

  • MarkdownTool:生成的 ASCII Art 可粘贴到 Markdown 代码块中作为标题
  • LoremTool:占位文本可与 ASCII Art 横幅组合,生成完整的文档骨架
  • DiffTool:对比不同字体的渲染结果,观察差异
  • HashTool:将 ASCII Art 作为输入计算哈希,验证复制完整性

九、局限性与改进方向

当前实现仍有改进空间:

  1. 字符集有限:仅覆盖大写字母与常用标点,小写字母、中文无对应字形
  2. 字体种类少:仅 3 种字体,FIGlet 社区有数百种字体可选
  3. 无紧排模式:仅支持全宽 + 间距,未实现 FIGlet 的 kerning/smushed 模式
  4. 无 ANSI 颜色:终端场景常需 ANSI 颜色码,当前仅输出纯文本

未来可考虑:动态加载 FIGlet .flf 字体文件、支持小写字体、添加 ANSI 颜色输出、支持多行文本逐行渲染。

十、总结

ASCII Art 是计算机文化中独特的视觉表达形式,从电传打字机时代延续至今。FIGlet 让字体渲染进入自动化时代,但完整 FIGlet 实现复杂度高。本工具采用「精简字体 + 纯原生 TS 渲染」策略,内置 3 种字体覆盖常见场景,Bundle 仅 19KB,在零依赖前提下提供了实用的 ASCII Art 生成能力。

无论是 GitHub README 标题、终端启动横幅,还是源码注释装饰,ASCII Art 都能在纯文本环境中提供视觉冲击力,是开发者工具箱中值得保留的小工具。