字体 or 字体族?
我们所熟知的"字体"通常包括多个变体,如粗体、斜体等。
例如你可能熟悉(听说过)的Times字体,其实它有多种变体,包括TimesRegular、TimesBold、TimesItalic、TimesBoldItalic等。
Times的这些变体各自都是一个字型,我们通常所说的Times,其实是这些不同字型的统称。也就是说,Times其实是一个字体族,而不是一个字体。
五种通用字体族
CSS中定义了五种通用字体族:
- serif:衬线字体。这种字体中的字行宽度各异,而且字符笔划末尾有装饰(短线)。
- sans-serif:无衬线字体。这种字体中的字行宽度各异。
- monospace:等宽字体。等宽字体中的字形宽度一样。
- cursive:手写字体。这种字体尝试模仿人类笔迹或手写体。
-
fantasy:奇幻字体。这类字体没有统一的特征,无法归类为上述的4种字体,就称为奇幻字体。
这五类字体族不代表某一个具体的字体,而是当你在 CSS 中指定字体族的时候,系统就有可能在字体族中找出一种字体来显示。
衬线体字体 VS 无衬线字体
衬线体和无衬线体最明显的区别,衬线体的结构中通常存在粗/细对比,无衬线体则往往有一致的粗细。
在字的笔画开始、结束的地方有额外的装饰,而且笔画的粗细会有所不同的,就是衬线体,反之则是非衬线体。
如果说衬线体(serif)的风格是传统和复古,那无衬线体(sans-serif)就是现代和中性了。
衬线体通常在传统印刷行业中往往能获得更好的可读性,在大段落文章中使用衬线体能提供中文对字母的参照。
无衬线体的使用场景往往更加“随意”,相比“严肃”的衬线体,使用范围更广且更符合大众化。
font-family
font-family 允许通过给定一个有先后顺序的,由字体名或者字体族名组成的列表 来为选定的元素设置字体。
浏览器会选择列表中第一个该计算机上有安装的字体,或者是通过 @font-face 指定的可以直接下载的字体。
font-family 列举一个或多个由逗号隔开的字体族,语法如下:
/* 一个字体族名和一个通用字体族名 */
font-family: "Gill Sans Extrabold", sans-serif;
font-family: "Goudy Bookletter 1911", sans-serif;
/* 仅有一个通用字体族名 */
font-family: serif;
font-family: sans-serif;
font-family: monospace;
font-family: cursive;
font-family: fantasy;
font-family: system-ui;
font-family: emoji;
font-family: math;
font-family: fangsong;
注:字体族名可以包含空格,但包含空格时应该用引号。
字体fallback机制
当font-family中指定的字体找不到(或者某些文字不支持这个字体)时,那就接着往后找。
比如:
.font-family-test {
font-family: "PingFang SC", "Microsoft Yahei", monospace;
}
上述代码的意思:该文字在Mac & ios 平台用苹方字体,在Win平台用微软雅黑,如果这两个都没有,就由浏览器找一个等宽的字体进行渲染。
由于无法保证用户的计算机是否安装font-family指定的字体,通常会在font-family列表中最后添加一个通用的字体(兜底)。
字体选定时机
字体的选定不是在发现用户计算机上安装的列表中的第一个字体时停止。相反,对字体的选择是逐字进行的。也就是说即使某个字符周围都在某个字体中可以显示,但该字符在当前的字体文件中没有适合的图形,那么会继续尝试列表中靠后的字体。
这里有一个例子:https://codepen.io/drowned-fish/pen/eYvNKLJ?editors=1100
font-weight
font-weight 指定字体的粗细。
- normal:正常粗细。与400等值。
- bold:加粗。与700等值。
- lighter:比从父元素继承来的值更细。
- bolder:比从父元素继承来的值更粗。
- <number>:介于 [100, 900]之间的数字,数字越大字体越粗。数值采取离散式定义(使用 100 的整倍数)。数值为实数,非 100 的整数倍的值将被四舍五入转换为 100 的整倍数,遇到 *50 时,将向上转换,如 150 将转换为 200 。
注意:并不是所有字体都支持font-weight:<number>
font-weight 支持 100 - 900来设置字体的粗细,100是最细的,900是最粗的。
当然,并不是所有的字体都会提供三种(以上)的粗细。 一些字体只提供 normal 和 bold 两种值。
也就是说,100、200、300、400和500可能都对应于同样细的变体;600、700、800、900则对应较粗的变体。
如果一个字体只有 normal 和 bold 两种粗细值选择,指定粗细值为 100-500 时,实际渲染时将使用 normal,指定粗细值为 600-900 时,实际渲染时将使用 bold 。
font-size
font-size 应该是在实际开发中使用比较多的 CSS 属性之一了。
它用来指定字体的大小,可以是绝对大小或相对大小,默认继承父元素的 font-size。
绝对大小
font-size 的绝对大小分为关键字和 px。
关键字
font-size 支持的绝对大小关键字有7个,分别为:
- xx-small:medium 的 0.6 倍 ( h6 预设值 )
- x-small:medium 的 0.75 倍
- small:medium 的 0.8 倍 ( h5 预设值 )
- medium:预设值,等于16px ( h4 预设值 )
- large:medium 的 1.1 倍 ( h3 预设值 )
- x-large:medium 的 1.5 倍 ( h2 预设值 )
- xx-large:medium 的 2 倍 ( h1 预设值)
px
px 是我们在web开发中常用的单位之一。
当你需要精确地设置字体的大小时,用px设定字体大小是一种好方法。
font-size: xx-small;
font-size: x-small;
font-size: small;
font-size: medium;
font-size: large;
font-size: x-large;
font-size: xx-large;
font-size: 16px;
当然,绝对单位除了 px 外还有一些大部分人都没有用过的单位,如:cm、mm等,这里不做阐述。
相对大小
font-size 的相对大小同样也有关键字和相对单位。
关键字
- larger
- smaller
顾名思义,larger 和 smaller 根据父元素的字号增大或减小一定比例,具体比例由浏览器决定(1.2是建议的比例)。
相对单位
- %
- em
- rem
百分数在某种意义上与相对大小关键字很像。百分数始终根据继承自父元素的字号计算。与相对关键字相比,百分数更能细致地控制字号。
CSS还把长度单位 em 定义为等效于百分数,同样是根据继承自父元素的字号计算。
对字号而言,1em === 100% 。
因此,下面两个规则得到的结果一样:
rem 的大小是根据根元素定义的 font-size 来计算最终大小,1rem 等于根元素的字体大小。
/** 假如根元素字体大小是14px */
html { font-size: 14px}
.test-box {
font-size: 1rem; /** 14px */
}
字形
说字形你可能比较陌生,但是 font-style 你应该使用过。
font-style属性的作用很简单:在normal(常规)、italic(斜体)和 oblique(倾斜体)之间做出选择。
font-style的默认值是normal,这表示竖直体。
- normal:选择font-family的常规字体。
- italic:斜体,如果当前字体没有可用的斜体版本,会选用倾斜体(oblique )替代。
- oblique:倾斜体,如果当前字体没有可用的倾斜体版本,会选用斜体(italic )替代。
请注意,不是所有的字体都有确切的 oblique 和 italic 字形,如果没有的话,浏览器也会通过使用现有的字形来模拟所缺少的字形。
italic和oblique的区别
简单来说,斜体是一种单独的字型,各字母的构造有些改动,体现外观上的不同。(大部分字体italic和oblique没区别)
- italic 是使用当前字体的斜体字体,而 oblique 只是单纯地让文字倾斜。
- 如果当前字体没有对应的斜体字体,则退而求其次,解析为 oblique,也就是单纯形状倾斜
在下面这个例子中,这两种字形完全没区别:
引入字体
一般而言,使用 @font-face 来引入自定义字体。
@font-face支持的描述符有:font-family,src,font-style,font-weight,unicode-range,font-variant ,font-stretch 等。
必须的描述符
上述描述符中有两个是必须的:font-family 和 src。
- font-family,对导入的字体设置一个名称,方便其他地方使用该字体。
- src,为定义的字型提供一个或多个源(字体文件的地址)。如果有多个源,以逗号分隔。
@font-face {
font-family: 'My Font';
src: url('xxxx.otf'), url('/fonts/xxx.otf');
}
其他字体描述符
- font-style: 区分常规、斜体和倾斜字型,默认值 normal
- font-weight:区分不同的字体粗细,默认值 normal
- font-stretch:区分不同的字符宽度(例如紧缩和加宽),默认值 normal
- unicode-range:定义指定字体中可用的字符范围 ,默认值 U+0-10FFFF
字体显示时间轴
字体显示时间线基于一个计时器,该计时器在用户代理尝试使用给定下载字体的那一刻开始。时间线分为三个时间段,在这三个时间段中指定使用字体的元素的渲染行为。
阻塞期(Block Period)。在此期间如果字体没有加载完成,那么浏览器会使用 font-family 指定的字体列表中的后备字体(Fallback)进行渲染,但是显示为空白,也就是对于用户是不可见的。在此期间字体加载完成之后才能正常显示该字体。
交换期(Swap Period)。跟阻塞期类似,但是在这个时期内,它会在字体加载时,先用后备字体渲染文本并显示出来(而不是显示空白),在此期间字体加载完成之后才能正常的显示该字体。
失败期(Failure Period)。如果字体加载失败,则使用后备字体显示文本。
font-display
font-display 属性决定了一个 @font-face 在不同的下载时间和可用时间下是如何展示的。有以下几个取值:
auto 。font-display 的默认值,字体的加载过程由浏览器自行决定,不过基本上和取值为 block 时的处理方式一致。
block 。在字体加载前,会使用备用字体渲染,但是显示为空白,使得它处于阻塞期(一般在3s,有些浏览器会无限阻塞),当字体加载完成之后,进入交换期,用下载下来的字体进行文本渲染。如果阻塞期仍然没有加载完字体,那么直接就进入交换期,显示后备字体(而非空白),等字体下载完成之后直接替换。
swap 。基本上没有阻塞期,直接进入交换期,使用后备字体渲染文本,等用到的字体加载完成之后替换掉后备字体。
fallback 。阻塞期很短(大约100毫秒),也就是说会有大约 100 毫秒的显示空白的后备字体,然后交换期也有时限(大约 3 秒),在这段时间内如果字体加载成功了就会替换成该字体,如果没有加载成功那么后续会一直使用后备字体渲染文本。
optional 。与 fallback 的阻塞期一致,但是没有交换期,如果在阻塞期的 100 毫秒内字体加载完成,那么会使用该字体,否则直接使用后备字体。这个就是说指定的网络字体是可有可无的,如果加载很快那么可以显示,加载稍微慢一点就不会显示了,适合网络情况不好的时候,例如移动网络。
在了解 font-display 之后,那么我们应该不难看出来,对于大部分情况应该把它的值设置为 swap ,这样在加载网络字体期间,先使用后备字体进行渲染,加载完成之后再替换为指定的网络字体。
仅需了解的属性
font-stretch
有些字体族中的变体可能具有较宽或较窄的字母形式,这种变体存在的目的是在同一个字体族中提供瘦体和胖体,font-stretch 属性用于选择这样的变体。
这个属性并不会通过伸展/缩小而改变字体的几何外形,它仅仅意味着当有多种变体可供选择时,会为字体选择最适合的大小。
仅当使用的字体族中有宽体和窄体时,这个属性才起作用。
font-kerning
有些字体定义了字符之间相对位置的数据,即字距。
如果字体中没有字距数据,font-kerning没有任何作用。
- none:禁用字体中的字距信息。
- auto:浏览器来决定是否使用字体字距。比如,一些浏览器会在小字体的情况下禁用字距,因为这会使得文本可读性下降。
- normal: 应用字体中的字距信息。
font-variant
font-variant:指定是否以 small-caps 字体(小型大写字母)显示文本。在 small-caps 字体中,所有小写字母都将转换为大写字母。但是,转换后的大写字母的字体大小小于文本中原始大写字母的字体大小。
例子如下:
如何优化中文字体的加载性能?
在开发过程中,有时我们希望使用一些美观的字体。若使用非操作系统支持的Web安全字体,就需要额外加载字体文件。如果要使用汉字字体,由于汉字数量众多,单个字体文件体积往往会达到3、4MB。
那么是否有手段从该字体文件中将使用到的文字取出?
提取字体中的部分字型,最小化打包字体
字体文件中包含的绝大多数字符都是不会被用到的。如果有办法将用到的文字从字体文件中单独拿出来组成一个子集字体文件,则能减轻不必要的网络加载资源浪费。
从意思上来看,有点类似webpack中的tree shaking,但是我们这是生成一个子文件。
这里就要提到一个工具:
http://ecomfe.github.io/fontmin/
Fontmin 是一个纯 JS 实现的字体子集化方案,思路是将无用的字形移除,具体原理有兴趣的可以看下这篇文章:Web 中文字体性能优化实践
当然,从根源上考虑,应该尽量减少项目中的在线字体数量 ,特别是对于移动端项目,要在性能、体验和美观三者之间有个权衡。
总结
文章介绍了什么是字体、有什么字体、在 CSS 中如何使用自定义字体,字体的加载机制,最后给出了缩减字体文件的解决方案。