之前在使用一些开源项目时,经常会看到在控制台输出项目大大的 LOGO。例如:

  • hexo minos 主题启动时在控制台里会显示「MINOS」文案
  • fis3 启动时也会有显示「FIS」

添加这种大号「艺术字」可以达到「品牌露出」的效果,当然,也是程序员特有「情趣」的体现。

但它们的实现方式无外乎把编排好的 Logo 通过 console.log 输出。这种方式问题在于它几乎没有任何复用能力,而且一些需要转义的情况还会导致字符串的可维护性极差。因此,我花了一个周末的时候,实现了一个易用的、可复用的控制台「艺术字」lib。这样,下次有新的需求,只需要把正常的文本传给它,它就可以帮你自动编排与打印

1. 目标

正如上节所说,目前一般项目的做法都是自定写一串特定的文本,例如 minos:

还有 fis3 这种由于需要添加转义所以显得凌乱不好维护的

这种些方式都是通过「硬编码」来实现的,如果有了新项目或需求变动还得重新编排调整。

因此,准备实现一种能够根据输入的字符串进行自动排版展示的控制台「艺术字」打印库,例如通过 yo('yoo-hoo') 就会输出:

下次如果文案改了,直接换下字符串参数就行 —— yo('new-one')

总结来说,就是实现一个通用的、可复用的控制台「艺术字」打印功能。基于这个目标开发了 yoo-hoo这个库。

下面来说说大致怎么实现。

2. 如何实现

和其他字体显示的需求类似,我们可以将功能抽象为三个部分:

  1. 字体库的生成
  2. 字体的排版
  3. 字体的渲染

这里我们先说一下字体的渲染。

2.1. 字体渲染

之所以先说这部分,是因为它会影响排版信息的输出格式。

其实字体渲染这部分并没有什么特别的,我们在控制台这个环境,受限于 API,基本就是使用 console.log 来将内容「渲染」到屏幕上。不过,正是这里的「渲染」形式的限制,会倒推我们的排版方式。

我们知道,控制台基本都是单行顺序渲染的,大致就是「Z」字型。同时,由于我们的「艺术字」会占据多行,所以最终的渲染不是按单个字顺序渲染的,需要先排好版,然后按行来逐步渲染到屏幕上。

这有点像是咱们常见的打印机。如果你要打印一个苹果,它会从上往下逐步打印出这个苹果,而不是直接像盖章那样直接印刷一个苹果。

下面我们会先介绍字体库的生成,而不是紧接挨着的字体排版。因为排版是一个承上启下的过程,当我们确定了上下游环节,这块的逻辑自然也就确定了。

2.2. 字体库生成

当我们想要实现可复用能力时,因此我们需要找到或者抽象出系统内逻辑上的最小可复用单元 —— 在这里显然就是字符。简单来说,对于输入字符串 JS 时,如果我们能找到对应的 J 和 S 的字符表示形式,辅以排版,理论上就有能力实现我们的目标。这有点像是咱们老祖宗的活字印刷术。

所以在字体库这里,我们会有一个字义与字型的映射。这个其实和咱们前端常见的字体文件内格式的思想一样,都需要有这么一个映射关系。

字型哪里来呢?好吧,我也是用了一个笨办法 —— 自己「手绘」 。举个例子,下面就是我「手绘」的 1:

绘制的过程是枯燥的,好再很多字型的局部是有一定复用的,简化了这项繁琐的工作。当然,这只是一次性的工作,一旦创建好一类「字体」,以后就不需要再重复这项工作了。

我把上面这个内容存在一个单独的文件中,目前直接以 .txt 为后缀,这就是我们的字体原始格式。之所以不放在 .js 中,是因为 JavaScript 中 是想要转义的,这样文本的视觉和最后的呈现效果就不一致了,不利于调试和维护。

原始字体文件分为两部分:

  • 上面第一行是字义,支持一个多个字义对应一个图形。例如 ·* 我使用了同一个图形。多个字义间空格分割,不换行。
  • 除去第一行,剩下的内容就是字型。

理论上,我们可以以这个原始字体文件来作为字体库了,通过 NodeJS 中的 fs 模块读取并解析文件内容即可得到映射关系。

但我希望它也能在非 NodeJS 环境(例如浏览器)中使用,所以不能依赖 fs 模块。这里做了一个原始文件的解析脚本,生成对应的 JS 模块。由于我们并不直接维护这些生成的 JS 模块,所以它的可读性不重要,可以设计数据格式的时候可以完全面向后续的排版流程。

首先实现一个简单的解析器来解析第一行的字义。这也类似一个词法解析器,但由于语法规则极其弱智(简单),所以也就不用多说了,大致如下:

下面就是处理字型部分。之所以需要处理字型,是因为上面提到的转义问题。由于我们在原始格式中使用了 来进行字型展示,而将其直接放入生成的 JS 文件中这个 就变为了转义符,要想正常展示需要变为 。一种方式是正则匹配,将所有源文本中的 替换为 再写入。但我选择了另一种方式。

将字符通过 .charCodeAt 方法转为 char code 存储,读取字体信息时再通过 String.fromCharCode 转回来。原来的字符串变成了数字类型的数组,这样就没有特殊字符的问题了。最后,通过拼接文本并生成 JS 文件来将原始的、利于人维护的字体文件,转成了编译 JS 工作的模块。

其中 defs 就是这个字型对应的字义列表,codes 则是字型的 char code 数组,所有的字体都被放在一个 JS 文件中。

这里提一下,第 3 行的 parsedFonts 就是遍历所有原始字体文件解析到的内容,因此得到这部分也是需要通过 NodeJS 的 fs 模块来递归读取源文件目录下的字体文件的。算是基操,就不用展开了。

由于这部分是可以提前解析编译的,一旦生成了 JS 模块后就不会对 NodeJS 运行时有依赖,所以保证了其依然可以运行在浏览器中。

2.3. 字体的排版

我们的字体格式确定了,目标的渲染方式也确定了。最后就可以填充这部分的逻辑实现了。

具体排版上会遇到一些细节点,例如不等高字体的空行填充、最大行宽的换行判断(需要用户执行行宽),不过这些都是小点,处理也不太复杂。这里可能介绍一下稍有特殊的一块 —— 字间距调整。

我们知道,一些艺术字的倾斜程度可能很大,例如这个字符「1」:

如果按简单的矩形型包围盒来分配空间,大概会是下面这样:

前后两个字体,即使设置为最小间距(0),仍然会距离很远,这样就破坏了一定的显示效果。例如上图中我两个包围盒间距其实只有 1,但看起来就很大。我们实际希望的可能是下面这样:

间距为 1 时,两个字符「1」调整为在最近的地方间距为 1。如果要更宽的效果可以设置更多间距。这个处理起来主要就是需要算出最大的「挤压空间」(即两个盒子最大支持的交叉空间)。最开始渲染的时候说了,我们是按 console 出的行来存储的与打印的,举个例子,这个「1」高度为 8 ,所以渲染的时候就是一个 8 个元素的字符串数组:

渲染的时候直接 lines.forEach(l => console.log(l)) 即可。

注意,为了便于读者阅读,上面的 lines 数组内的字符串我没有加上转义,它是不合法的!只是为了展示起来更便于阅读理解,实际中不能这么写。

最大缩进(缩进这个词不准确,但希望大家能够理解那个意思)的计算只需要知道之前的每个 line 尾部对应有多少空格,同时需要再其后新添加字符每个 line 前面又分别有多少空格,综合两者,再遍历所有的 line 取一个最小值即可:

最后 calcIndent 方法返回的就是新字符需要向前缩进(或者说缩紧)的值。最后渲染的时候根据这个值来调整每行连接时添加的空格数即可。

捎带一提,之前的字体格式 load 进来会被转换为类似字典的格式 —— 字义作为 key,字型等一系列属性作为 value:

这样遍于 split 完用户传入的字符串后,更简单的索引到对应的字型和字体信息。

2.4. 其他

当然,其他还会有一些工作,包括

  • 支持颜色
  • 支持返回排版完的 lines 让用户自己渲染
  • 支持用户自定义调整字间距

这些目前实现上遇到的问题不大,篇幅原因也就不说了。具体的代码可以在 Github 上看到。

3. 总结

实现可复用的控制台“艺术字”功能,总的来说并没有太多复杂的点,整体的流程模型就是

生成字体库 --> 字体排版 --> 渲染文本

这对于前端来说应该是非常好理解的。

做这个项目也确实是自己在工作中希望给一些库加上这种 logo 或者 banner 展示,但每次重复枯燥的工作确实令人反感。所以想了下可行性之后就搞了 yoo-hoo 这么个小玩意儿,如果大家也遇到类似的问题,希望能有所帮助。

4. 最后

目前 yoo-hoo@1.0.x 内置了一套 26 个字母(A-Z)、10 个数字(0-9)、· * - | 这些字符的字体库。

考虑到单一的字型和有限的字体量肯定不能满足所有需求,所以开发时代码结构就留下了支持外部扩展的模式。

后续可以把 2.2 节中的字体源文件解析工具独立出来,支持用户「手绘」自己的字型,用工具生成对应格式后,将字体的 JS 模块传入 yo 方法中作为扩展字体加载。

字体源文件的「手绘」虽有成本,但所见即所得,编写难度不大 同时也算是一劳永逸。

原作者姓名:alienzhou
原出处:segmentfault
原文链接:如何实现可复用的控制台“艺术字”打印功能_精益前端 - SegmentFault 思否

js打印到控制台_如何实现可复用的控制台“艺术字”打印功能相关推荐

  1. java打印星型_初识java java入门知识 基础知识 打印各种星型图形 源代码

    今天给大家带来的是初级Java基础部分的知识:包括初识Java.变量.常量.数据类型.运算符.各种选择结构.循环结构.数组等Java的基础语法部分!最后还有****循环结构的进阶****,步骤超详细, ...

  2. python 调用控制台_如何使用Python的交互控制台

    简介 Python的交互控制台(也叫做Python解释器,或是Python Shell)为程序员提供了"运行指令"和"不创建文件测试测试代码"的快速途径. 交互 ...

  3. python日志模块为什么打印到界面_如何将外部模块的日志消息打印到主Python模块的终端窗口?...

    我正在写一个Python命令行程序.在 有一个主Python脚本文件,用作入口点.当用户运行这个脚本时,它将执行一些外部Python脚本文件.外部Python脚本文件也可以执行其他外部Python脚本 ...

  4. qt 隐藏控制台_带可选GUI的Qt控制台应用程序

    简单地说这行到你的亲文件: CONFIG += console 在Qt5.x也从Win7到Win10你可以做这样的事情: //includes #include #include #include # ...

  5. java调用打印预览_急求一个用Java实现的打印及打印预览功能的Demo

    展开全部 package com.szallcom.tools; import java.awt.BorderLayout; import java.awt.Color; import java.aw ...

  6. 打印表单_超市生鲜日常作业表单,打印出来就能用!

    零售商要在日趋激烈的市场竞争中生存.发展,必须要树立低价格形象.加强吸引客流的能力并不断增强顾客的忠诚度.低价格形象是一种市场营销策略,是零售商吸引顾客的主要手段. 显然蔬菜.水果作为顾客的目标性购买 ...

  7. python如何打印txt文件_在Python中的.txt文件中打印特定行?

    I have got a .txt file that contains a lot of lines. I would like my program to ask me what line I w ...

  8. jq控制div是否展示_选择控制台_调度台之前,这几点你应该要看!

    随着现 代智能化技术的不断发展,各个领域都在打造与时俱进的智能办公环境.而提升智能办公环境怎么能少得了控制台和调度台这些智能化设备? 控制台_调度台效果展示 针对如何选择控制台.调度台.操作台等设备, ...

  9. JS数据结构与算法_链表

    上一篇:JS数据结构与算法_栈&队列 下一篇:JS数据结构与算法_集合&字典 写在前面 说明:JS数据结构与算法 系列文章的代码和示例均可在此找到 上一篇博客发布以后,仅几天的时间竟然 ...

最新文章

  1. 对于量子计算来说,99%的准确度足够吗?
  2. gradle 的 依赖管理(八)
  3. nyoj164——卡特兰数(待填坑)
  4. JavaFX之TableView的TableRow
  5. stk在计算机仿真中的应用_学习电路仿真:proteus电路仿真软件在ARM中的应用解析...
  6. python打包exe
  7. SQL Cookbook(读书笔记)No.2
  8. C#的rdlc报表分组汇总学习
  9. python求小于n的最大素数_枚举1--求小于n的最大素数
  10. 新乡医学院三全学院赴范县历史成就观察团
  11. 高德地图API:如何根据经纬度获取位置信息
  12. Python新手接了第一个副业单子,2小时完成:Python修正excel表格数据
  13. 《Python编程从入门到实践 第二版》第十六章练习
  14. STM32F4内的FLASH和RAM
  15. Debian Bullseye 更新源备份
  16. java实验报告(实验三)
  17. NDN网络学习笔记(一)——NDN基础
  18. SLIC图像超像素分割算法解析
  19. Java利用Set集合去重复
  20. java中内边距跟外边距,padding和margin——内边距和外边距

热门文章

  1. 玩转springboot2.x之自定义项目内自动配置
  2. 1小时搞懂设计模式之策略模式
  3. spring的三种启动方式
  4. thymeleaf的属性优先级
  5. android 弹出键盘引起的问题
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的个人记账管理系统
  7. MySQLSyntaxErrorException: Table 'taotao.tbuser' doesn't exist
  8. Java内存管理(一)--内存分区
  9. 2019.1.31及以前
  10. svn .a文件上传不了