> ## 文章索引
> 在这里获取全部文章的索引：https://lrd.im/blog/llms.txt
> 这是 lrd.im 的个人博客，记录设计、技术与思考。

# 等宽数字 Monospaced Numbers 在界面中的使用

![封面图](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/01.png)

在[上一篇文章](/blog/2020-10-04)的3.2章节里，提到了 AntD 组件会全局默认使用等宽数字。这对我来说是比较新的概念，所以也花了点时间观察、总结了等宽数字在界面上的几个使用场景，以及普遍的实现方式，正好也可以作为文章记录下来。

![文章结构示意图：「等宽数字 Monospaced Numbers」分为三个子主题——概念介绍（Definition）、使用场景（Cases）、实现方式（Solution）。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/02.png)

## 一、概念介绍

这里介绍两个概念，常见的**比例字体 (Proportional Font)**，以及这篇文章所介绍的**等宽字体 (Monospaced Font)**。

### 1. 比例字体 (Proportional Font)

在一些字体里面，从 0 到 9 、从 A 到 Z，每个数字所占的宽度会不同，例如我去年发表[这篇文章](/blog/2019-11-17)里介绍到的苹果系统默认的字体 SF Pro Display，默认情况下使用的是**比例字体 (Proportional Font)**，即每个字符占位宽度都不尽相同，会受到对应的字符形状而影响。

![比例字体宽度对比示意图：6 个 "1" 的总宽度远小于 6 个 "8" 的总宽度；同理，6 个小写 "i" 的总宽度远小于 6 个大写 "M" 的总宽度，直观说明比例字体中不同字符占位宽度各异。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/03.png)


### 2.等宽字体 (Monospaced Font)

等宽字体顾名思义即每个字符所占的宽度一样，由下图可见，等宽字体会在原本较窄的字体上添加一些衬线来作为平衡，撑大容器。

![等宽字体对比示意图：六个「1」与六个「i」宽度相同，六个「8」与六个「M」宽度相同，说明等宽字体中窄字符会通过衬线撑宽，使每个字符占位一致。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/04.png)

因为每个字符之间的间距相对较大，便于缩进对齐，且每个字符有极高的辨识度，不会出现在常见的字体中「1lI!」这种容易辨识错误的问题。换言之，这种字体为了提高辨识的精确度，却损失了字体原有高低错落的灵动性。所以常常出现在代码编辑器里，平常生活中很少见到。

通常这类字体名称带有「mono」，以便与其他字体区分。比如 SF Mono，PT Mono，JetBrains Mono 等等…

但这次我们讨论的是等宽数字，即在常见的比例字体下，透过字体的 [OpenType](https://zh.wikipedia.org/wiki/OpenType "维基百科对 OpenType 的介绍") 高级排版功能来实现仅数字等宽的效果，字母、标点符号不受影响，字符宽度仍然是富有韵律地变化。

![SF Pro Display 与 Gilroy 字体的比例数字与等宽数字对比示意图：两款字体均展示「123456」，上方为比例数字（各数字宽度不同），下方为等宽数字（各数字占相同宽度）。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/05.png)

## 二、使用场景

经过一段时间的观察和思考，我总结了三个适合使用等宽数字的场景。

### 1. 纵向数据展示—便于对比

如果页面内有多行纵向排列，且需要对比的数据，例如价格、容量、毫升、千克、摄氏度等，可以考虑使用等宽数字（如下图）。便于用户纵向对比各项数据，获得更佳的视觉体验。

![三个使用等宽数字的界面截图：支付宝账单列表（-19.90、-79.00、-248.09、-269.91）、iOS 天气周预报温度列（63–68°F）、Mac Finder 文件大小列（66–329 KB），数字均右对齐，纵向排列整齐便于对比。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/06.png)

不知道大家有没有从上面的例子发现，通常纵向排列时需要使用等宽数字的情况，还伴随着一个特点：文字右对齐。

即便时不需要对比的数据，当列表里出现右对齐的字段，使用等宽数字还有一个好处，可以最大限度地保障该字段的左边是对齐的。

### 2.动态变化的数字—防止抖动

非等宽数字的情况下，每个数字宽度不全相同，在纵向排列的时容易导致垂直参考线歪歪扭扭，无法对齐。这种字体应用在实时动态变化的数字字段时会带来更严重的后果 —— 界面元素抖动。

![音视频播放器进度时间 "50:36 / 93:39" 的界面截图，展示使用非等宽数字时，拖动进度条实时展示的时间变化过程中，动态数字变化会导致界面元素抖动错位，视觉体验不佳。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/07.gif)

就像上面的 GIF 图一样，如果某个动态变化的数字字段使用非等宽数字，该字段除了数据信息在变化外，宽度也会不停变化。假设字段行内有其他元素，或附近有依赖 padding 进行布局的元素，造成的影响就会比较大了。

所以，我近期也总结了一些适合使用等宽字体的动态变化数字的情景：

1. 时间：音视频播放器的进度条、秒杀倒计时、已录音/录屏时长等；
2. 各种物理单位：指南针、经纬度、温度、气压计、AR测距等；
3. 其他：金额、竞赛比分、进度百分比 、直播人气等。


![iOS 健康 App 的睡眠闹钟界面截图，显示就寝时间为今天下午 10:00。时间数字采用等宽字体展示，所以用户在拖拽滑块改动时间时，数字宽度不会跳动。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/08.gif)

![手机指南针 App 截图，显示方位角 138° 东南，坐标为北纬 23°8'20"、东经 113°17'32"，演示经纬度、方位角等动态变化时，数字宽度不会跳动。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/09.gif)

以上列举出来的只是冰山一角，可以考虑结合自身业务特点，列一个应用在公司内部的等宽数字字段表，保障每处设计都有迹可循。

### 3. 计算数字宽度的布局—准确定位

某个只展示字段的数字，想要固定该字段宽度时，需要同时使用 CSS 内的字体大小单位`ch`以及等宽数字样式来获得准确的宽度。

`ch`是以字符 0 的宽度作为标准单位，`1ch`的宽度等于当前网页字体下，一个数字0的宽度，是相对单位。会随着字体不同而略有变化。

就像前段时间公司一个需求里，要做到下图中的布局。序号最大 3 位数，宽度就可以定位`3ch`，确保设置的字段宽度不会太大或太小。同时使用等宽字体的特性，可以避免在序号为 10，11，12 时，数字字段与卡片的距离不同的问题。

![三列对比示意图：序号宽度较小时显示「短」、较大时显示「长」、设为 3 个字符（3ch）时显示「刚好」，说明将序号字段宽度设为 3ch 配合等宽数字，可精准适配最多 3 位数的序号而不偏大或偏小。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/10.png)

## 三、实现方式

不管在是在设计软件或落地到手机界面上使用等宽数字，一个大前提是该字体支持 [OpenType](https://zh.wikipedia.org/wiki/OpenType "维基百科对 OpenType 的介绍") 高级排版功能并支持等宽数字的特性，例如笔者常常批判的 PingfangSC 就不支持。

### 1.在设计软件上使用等宽数字

**Sketch**: 选中特定的字体后选择在顶部菜单栏选择「Text」 — 「OpenType Features」 — 「Number Spacing」 — 「Monospaced Numbers」即可转换为等宽数字。

![Sketch 菜单栏操作截图：依次展开 Text → OpenType Features → Number Spacing，最终选中「Monospaced Numbers」选项，背景可见 SF Pro Display 字体的大号数字示例。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/11.png)

实际上在「OpenType Features」这个选项里面还有其他可能会用到的 OpenType 高级排版选项，比如平方、立方、分数等。

**Figma**: 选中特定的字体后选择右侧选项栏里的「Text」 — 「Type details」 — 「Numbers Style」内选择右边很明显是等宽数字的图标，即可转换为等宽数字。

![Figma 的 Type details 面板截图，展示在 Numbers > Style 中选择等宽数字图标的操作位置，画布上的 "12345" 文本同步呈现等宽数字效果。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/12.png)

> Figma 在 2019 年开始爆红之后我也轻度使用了一会，发现各方面使用体验都超越 Sketch，起码用起来没那么卡，响应速度比较快。这次用到的 OpenType 高级排版功能，Figma 内可以快速预览设置某特性之后的效果，反观 Sketch，只能逐个点击，效率太低了。

### 2. 等宽数字在界面上的实践

落地到界面上使用等宽数字，Web 端可以直接使用 CSS 属性，两者选其一：

```css
font-variant-numeric: tabular-nums;  /*不支持IE浏览器*/
font-feature-settings: "tnum";
```

客户端上的实现方式可以参考[这篇文章](https://blog.usejournal.com/proportional-vs-monospaced-numbers-when-to-use-which-one-in-order-to-avoid-wiggling-labels-e31b1c83e4d0)，因为笔者工作上较少和客户端开发接触，没怎么了解过就在此不记录了。

或者用另一种取巧的方式，就是更换字体。像马蜂窝、有赞、美团等平台在标示价格时会用到另一种字体，恰好那种字体的数字自带等宽特性，所以不用进行其他设置也能直接使用。

![马蜂窝、有赞、美团外卖三款 App 的价格展示截图，红色虚线标注数字列对齐效果，展示这些平台所用字体自带等宽数字特性，无需额外设置即可实现数字对齐。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2020-12-25-e454336ec5b7/13.png)

常见的默认是等宽数字的还有 Roboto、Noto Sans、Helvetica Neue、DIN、微软雅黑、思源黑体、阿里巴巴惠普体等等。

等宽数字仅仅只是界面中呈现文字的一个微小的细节，前段时间做界面设计的时候越做越多疑问，遂上网找了些设计前辈的文章，这里推荐两篇比较有意思的文章：

- [移动阅读软件 ：中文排印上那些你不知道的事_阅文体验设计YUX-站酷ZCOOL](https://www.zcool.com.cn/article/ZMTE5NjYwOA==.html)
- [The Type — 文字 / 设计 / 文化   » 孔雀计划：中文字体排印的思路](https://www.thetype.com/kongque/)

