Web Fonts 的优化:F-mods
前面花了三篇的篇幅(Web Fonts vs. 系统字体、FOUT、FOIT 和 FOFT 和 Web Fonts 字体加载策略)围绕着 Web Fonts 的优化做了些探讨。但这些优化手段都还是会引起 Web 布局偏移。不过,CSS 的一些新特性,即 F-mods(字体度量覆盖描述符)来覆盖字体的一些度量参数,让系统字体字体(备用字体)和 Web Fonts 更接近,尽可能的减少布局偏移。
使用 F-mods 减少布局偏移
在了解如何使用 F-mods 减少布局偏移之前,我们有必要先花一点时间来简单的了解字体度量(Font Metrics)和 F-mods!
字体度量(Font Metrics)
正如 @Weston Thayer 在其博客《Intro to Font Metrics》开头提到:
字体文件包含大量关于字体的信息。
简单地说,字体是由一系列符号(通常称为字母或字符)组成的字体的数字表示形式。计算机读取字体文件,以便将屏幕上的字形渲染为像素。为了描述应该如何将所有这些单独的符号组合到单词、句子和段落中,字体设计人员用指标对字体进行编码。字体参数可以帮助计算机确定行与行之间的默认间距,上标(sup)和下标(sub)的高低,以及如何将两个不同大小的文本对齐。
通常情况,这些参数不会向用户公开,但我们可以使用一些工具来获取这些参数,比如 FontForge 和 Font Inspector:
注意,上图中的 ascender、descender 和 lineGap 接下来就会聊到。
另外,字体在 Web 上的使用是个复杂的体系,除了涉及一些排版本知识之外,还涉及一些字体知识,这里就对字体度量及相关的 CSS 属性不做过多解读,如果你想深究这一领域,下面这些资源应该值得阅读:
- Intro to Font Metrics
- FontForge Documentation
- Font Metrics Guide
- Technical Guidance Regarding Font Development And Production
- Vertical Metrics
- TrueType Reference Manual
- Fonts and text metrics
- Deep dive CSS: font metrics, line-height and vertical-align
- CSS Houdini: Font Metrics API Level 1
F-mods 简介
F-mods 是 Font Metrics Override Descriptors(字体度量覆盖描述符)的简称。它对应着 CSS Fonts Module Level 4 规范中的 @font-face
部分的第十一小节,即 默认的字体度量覆盖。简单地说,就是新增的四个 CSS 属性,对应着字体度量的四个描述符:
ascent-override
:对应字体度量中的 ascender 参数,用来覆盖分配给字体上升部分的尺寸,即上升指示(Ascent Metric);该描述符定义了字体基线(Baseline)以上的高度descent-override
:对应字体度量中的 descender 参数,用来覆盖分配给字体下降部分的尺寸,即下降指标(Descent Metric);该描述符定义了字体基线(Baseline)以下的高度line-gap-override
:对应字体度量中的 lineGag 参数,用来覆盖行间距,即行距指标(Line Gap Metric);该描述是字体推荐的行距或外部引线(External Leading)advance-override
:为每个字符设置一个额外的提前量,以帮助匹配行宽并防止单词溢出
特别声明:其中
advance-override
还未进入 W3C 规范,目前还仅处于提案中;而ascent-override
、descent-override
和line-gap-override
属性目前已在 Chromium 87+ 和 Firefox 89+ 中实现!
F-mods 作用
这四个描述符的组合可以让我们告诉浏览器在下载 Web Fonts 之前字符占用多少空间,来覆盖回退字体(系统字体)的布局,使其与 Web Fonts 相匹配。简单地说:
这四个描述符可以让你的系统字体更接近 Web Fonts!
其中 ascent-override
、descent-override
和 line-gap-override
描述符使我们 能够完全消除垂直布局的偏移,因为这三个描述符都会影响行高。
当计算行高时,字体的上升(Ascent)、下降(Descent)和行距(Line Gap)三指标会被设置为所用字体大小(font-size
)的给定百分比,即解析为给定百分比(也就是ascent-override
、descent-override
和 line-gap-override
的值,它们取值是个百分比值,除默认值 normal
之外)乘以字体大小(font-size
)。这也让我们可以使用它们来覆盖行框高度(Line Box Height)和基线位置(Baseline Position):
行框高度(Line Box Height) = 上升(Ascent) + 下降(Descent) + 行距(Line Gap)
基线位置(Baseline Position) = 行框顶部(Line Box Top) + 行距(Line Gap) / 2 + 上升(Ascent)
注意,上面这几个专业术语,可以通过下图找到他们在一个字体中对应的位置:
假设,我们分别给 ascent-override
设置值为 80%
,descent-override
设置值为 20%
,line-gap-override
设置值为 0%
,把这些参数套用到上面公式的计算公式中,就可以得出每个行框的高度为 1em
(假设使用的font-size
为 1em
),而基线位于行框顶部以下 0.8em
的位置。
注意,开发者可以在元素上设置
line-height
为非normal
的值。然而,line-height: normal
是一个重要的用例,我们希望在这种情况之下也能消除布局偏移。
而 advance-override
描述符允许我们减少水平布局的偏移,以及由不同的换行引起的垂直布局的偏移。这个描述为使用该字体的每个字符设置了一个额外的提前量。额外的提前量等于描述符的值乘以使用的字体大小。该描述符是在 CSS 的 letter-spacing
属性之外应用的。比如,如果我们在一个元素上设置了 font-size: 20px; letter-spacing: -1px
,并且设置了 adavance-override: 0.1
,那么最终会得到 20px * 0.1 - 1px = 1px
的字符间的额外间距。
如何获取 F-Mods 所需参数
字体对于很多 Web 开发者而言就是个迷,大多数情况只知道字体的名称,比如AlibabaSans102
。字体对应的参数对于我们来说并不是开放的,而我们要使用 ascent-override
、descent-override
、line-gap-override
(加上 advance-override
,这里暂不深入讨论这个描述符,因为到写这篇为止,还没有任何浏览器支持这个描述符)就需要字体对应的 上升(Ascent)、下降(Descent)和 行距(Line Gap)几个度量参数的值。
你可能会问,这些参数要怎么获取?除了找字体设计师提供这些度量参数之外,Web 开发者还有另一途径,那就是使用一些在线工具,比如前面提到的 FontForge 和 Font Inspector 工具。它们可以帮助我们快速获取需要的参数值。这里拿 Font Inspector 来举例,因为它相对来说更简单些。只需要在控制面板上传你将使用的 Web Fonts 文件,建议上传字体的原始文件,即 .ttf
或 .eot
格式。这个时候,你可以在 head
和 hhea
中折叠面板中找到这些所需的参数值:
注意:这些描述符的值应该根据目标 Web Fonts 的头部(head)和头部表(hhea)来获取。而且这些参数了会因系统不同有所差异,我们一般使用 hhea 中的参数值。
我们可以在 Font Inspector 的 hhead中找到四个关键值,它和这四个描述符有一定的映射关系:
- ascender:对应
ascent-override
- descender:对应
descent-override
- lineGap:对应
line-gap-override
- advanceWidthMax:对应
advance-override
除了这四个参数之外,每个字体还有一个 unitsPerEm
参数,其字面意思是字体的每一个Em的单位值,一般情况之下,unitsPerEm
的值为 1000
(在 Font Inspector 检测工具中的 head 折叠面板中可获取)。
从规范中,我们可以获知ascent-override
、descent-override
、line-gap-override
描述符的值是百分比值(%
),而从相关提案可以获知advance-override
是一个小数值(能不能用百分比描述,待规范发布)。例如,如果 Web Fonts 的 unitsPerEm=1000
,ascender=1041
,那么对应的 ascent-override
描述符值为 104.1%
(1041 / 1000 * 100% = 104.1%
)。
如果已获得了这些参数,我们可以借助 CSS 自定义属性,让它们在实际使用的时候变得更灵活些:
@font-face {
font-family: 'Arial'
src: local('Arial');
--unitsPerEm: 1000;
--lineGap: 10;
--descender: -237;
--ascender: 1041;
--advanceWidthMax: 815;
ascent-override: calc(var(--ascender) / var(--unitsPerEm) * 100%);
descent-override: calc(var(--descender) / var(--unitsPerEm) * 100%);
line-gap-override: calc(var(--lineGap) / var(--unitsPerEm) * 100%);
advance-override: calc(var(--advanceWidthMax) / var(--unitsPerEm));
}
如果你不习惯 Font Inspector工具,你还可以使用一款与其相似的工具 FontDrop,你只需要把要使用的 Web Fonts 拖到控制面板中,在 “Data” 一栏中可以获得该字体的所有信息,相应的在 hhea 一栏中能找到 "ascender"、"descender"、“lineGap” 和 “advanceWidthMax” 的值:
F-mods 的使用
有了这些字体度量参数之后,可知道了 F-mods 中每个属性对应的值是多少。这样一来,使用 F-mods 就容易地多了。假设你决定在使用 Web Fonts 时采用 F-mods 来减少字体带来的布局偏移,那么你只需要掌握这个基本诀窍即可: 在 @font-face
声明中使用 **src: local()
**定义备用字体(系统字体)。这样就可以覆盖备用字体的显示,以匹配 Web Fonts:
/* Web Fonts */
@font-face {
font-family: AlibabaSans102;
src: url("https://example.com/AlibabaSans102.woff2");
font-display: swap;
font-weight: 700;
font-style: normal;
}
/* 指定备用字体,local() 函数中指定备用的系统字体 */
@font-face {
font-family: 'AlibabaSans102-fallback'
src: local('Arial');
/* 使用 Font Inspector 或 FontDrop 相关工具,获取 Web Fonts 映射 CSS 字体覆盖描述符所需参数值 */
--unitsPerEm: 1000;
--lineGap: 10;
--descender: -237;
--ascender: 1041;
--advanceWidthMax: 815;
/* 使用 CSS 自定义属性 和 calc() 函数将值转换成 CSS 属性相匹配的值 */
ascent-override: calc(var(--ascender) / var(--unitsPerEm) * 100%);
descent-override: calc(var(--descender) / var(--unitsPerEm) * 100%);
line-gap-override: calc(var(--lineGap) / var(--unitsPerEm) * 100%);
advance-override: calc(var(--advanceWidthMax) / var(--unitsPerEm));
}
.price {
font-family: AlibabaSans102, 'AlibabaSans102-fallback'
}
简单地来看 ascent-override
、descent-override
、line-gap-override
描述带来的作用。为了省事,下面示例中直接使用 local()
调用了系统的 Arial Bold
字体,并且分别使用这几个描述覆盖本地系统的 Arial Bold
字体,创建新的字体。先来看 ascent-override
:
/* 使用 local() 调用系统的 “Arial bold” 字体,你可以在此更换成你喜欢的 Web Fonts */
@font-face {
font-family: "Arial Bold";
src: local(Arial Bold);
}
/* 使用 ascent-overrid 覆盖系统“Arial bold”字体,当作新字体 */
@font-face {
font-family: "Arial-Bold-fallback";
src: local(Arial Bold);
ascent-override: 71%;
}
.default {
font-family: "Arial Bold";
}
.adjusted {
font-family: Arial-Bold-fallback
}
效果如下:
红字文字是未调整的(对应 .default
,即使用的是 Arial Bold
),其中大写的 A
和 O
上面有空间(间距),而蓝色文本已使用 ascent-override
调整过的(对应 .adjusted
,即可使用了ascent-overrid
覆盖之后的 Arial-Bold-fallback
)的字体,所以蓝字文本中的大写 A
和 O
的上限高度与整个边界框是紧密相连的。
把上面示例中的 ascent-override
换成 descent-override
:
/* 使用 descent-override 覆盖系统“Arial bold”字体,当作新字体 */
@font-face {
font-family: "Arial-Bold-fallback";
src: local(Arial Bold);
descent-override: 0%;
}
效果如下:
从效果截图中不难发现,红色文本(未调整)的 D
和 O
基线下有空间,而蓝色文本是调整之后的,它的字母紧贴在基线上。
最后看 line-gap-override
描述符,把上面示例换成:
/* 使用 line-gap-override 覆盖系统“Arial bold”字体,当作新字体 */
@font-face {
font-family: "Arial-Bold-fallback";
src: local(Arial Bold);
line-gap-override: 50%;
}
效果如下:
红色文本(未调整)没有行距覆盖,基本上是 0%
,而蓝色文本已经被调整为 50%
,在字母上方和下方相应的创建了空间。
如果我们把这覆盖都放在一起,你将看到的效果如下:
/* 使用 local() 调用系统的 “Arial bold” 字体,你可以在此更换成你喜欢的 Web Fonts */
@font-face {
font-family: "Arial Bold";
src: local(Arial Bold);
}
/* 使用 ascent-overrid 覆盖系统“Arial bold”字体,当作新字体 */
@font-face {
font-family: "Arial-Bold-fallback";
src: local(Arial Bold);
ascent-override: 71%;
line-gap-override: 50%;
ascent-override: 0%;
}
.default {
font-family: "Arial Bold";
}
.adjusted {
font-family: Arial-Bold-fallback
}
最终效果如下:
我们来看一个真正的 Web Fonts 被 F-mods 覆盖后的效果,就拿AlibabaSans102
为例吧:
/* Web Fonts */
@font-face {
font-family: AlibabaSans102;
font-weight: 700;
font-style: normal;
font-display: swap;
src: url("https://g.alicdn.com/eva-assets/8f07c38aa173457f747f15a8774161a4/0.0.1/tmp/font/0ce464d2-bb11-41c8-8470-0049cea5f6b1.woff2") format("woff2");
}
/* 系统 Arial bold */
@font-face {
font-family: "Arial Bold";
src: local(Arial Bold);
}
/* 覆盖系统 Arial bold*/
@font-face {
font-family: Arial-Bold-fallback;
src: local(Arial Bold);
descent-override: 0%;
ascent-override: 72%;
line-gap-override: 3%;
}
/* 覆盖 Web Fonts */
@font-face {
font-family: AlibabaSans102-fallback;
src: local(Arial Bold);
--unitsPerEm: 1000;
--lineGap: 10;
--descender: -237;
--ascender: 1041;
--advanceWidthMax: 815;
ascent-override: calc(var(--ascender) / var(--unitsPerEm) * 100%);
descent-override: calc(var(--descender) / var(--unitsPerEm) * 100%);
line-gap-override: calc(var(--lineGap) / var(--unitsPerEm) * 100%);
advance-override: calc(var(--advanceWidthMax) / var(--unitsPerEm));
}
最终效果如下:
@SimonHearne 在 Codepen 上提供了一个“加载你的 Web Fonts 和备用字体(系统字体),并且使用 F-mods 的示例”。可以查看到他们之间的差异:
当然,你也可以借助 CSS 自定义属性和一点 JavaScript 脚本,构建一个动态的 F-mods 使用案例。这里就不演示了,感兴趣的可以自己尝试一下。
F-mods 也不是万能的
F-mods 只是修正了垂直方向的间距和位置。这也意味着字符间距和字母间距仍然需要处理,否则你可能会在不同的点上断行,导致元素高度的改变,从而导致布局的偏移。不幸运的是,CSS 的 letter-spacinng
和 word-spacing
并不能直接用于 @font-face
规则中,因此,我们需要在元素上单独使用。正如 @SimonHearne的示例中,就单独在一个选择器中使用了这两个属性:
/* Web Fonts */
@font-face {
font-family: custom-font;
src: url('https://simonhearne.com/assets/fonts/dosis-v17-latin-variable.woff2') format('woff2');
}
/* 使用F-mods调整后的备用字体 */
@font-face {
font-family: fallback-font;
ascent-override: 100%;
descent-override: 20%;
line-gap-override: normal;
advance-override: 10;
src: local(Arial);
}
/* 调整字母和单词之间间距 */
.fallback {
letter-spacing: -1.1px;
word-spacing: -0.2px;
}
这个工作稍微麻烦一些。如果你为了调整字母和单词之间间距,额外使用letter-spacing
和word-spacing
属性,那么在 Web Fonts 加载成功之后,需要从你的样式表中删除这两个规则。这个时候你就需要使用 CSS Font Loading API 或 FontFaceObserver了,当然,你也可以直接使用 JavaScript 来控制,只是略为麻烦一些。
未来可用于 @font-face
新特性
正如 F-mods 中所描述的一样,ascent-override
、descent-override
、line-gap-override
和已废弃的advance-override
都是用于 @font-face
规则中的 CSS 特性。他们都是用于服务 Web Fonts,主要目的 就是使用 CSS 来调整字体的度量参数,让备用字体(系统字体)更匹配 Web Fonts,从而减少 Web Fonts 引起的布局偏移。
值得大家庆幸的是,在 CSS Fonts Module Level 5 规范中又为 @font-face
规则添加了几个新属性。比如,用来覆盖字体上标(sup)和下标(sub)的 superscript-position-override
、 subscript-position-override
、superscript-size-override
和 subscript-size-override
描述符。虽然这几个属性还没有得到任何浏览器的支持,但对于 Web 开发者而言,这是希望。
除此之外,还新增了 size-adjust
描述符,它允许我们调整字形的比例系数(百分比)。该描述符取代了前面提到的 advance-override
描述符。正如 @Addy Osmani在他自己推特上,是这样描述 size-adjust
:
我们着重来了解一下 size-adjust
描述符。主要是因为,size-adjust
已经得到了 Chromium 92+ 和 Firefox 89+ 的支持:
不过需要开启相关的功能。比如在Firefox浏览器地址栏输入about:config
,然后搜索layout.css.size-adjust.enabled
,并且将其设置为 true
。重启浏览器之后你就可以在 Firefox 浏览器中看到 size-adjust
描述符的效果了。
size-adjust
先来看一个有关于 size-adjust
的视频:
上面的视频中,字体大小是一致的,都是64px
,不同的是每个标题使用了不同的字体。视频中左侧图是没有使用 size-adjust
的效果,文本会因为字体不同,对应的所在区域大小也不同。而右侧是使用了size-adjust
的效果,确保了不同字体的最终尺寸都是 64px
。从视频中可以明显看到,左侧因字体不同造成布局偏移,而右侧则没有。
那么,size-adjust
如何使用呢?其实,他的使用并不复杂,和前面介绍的 F-mods 使用差不多。比如下面这段 CSS:
@font-face {
font-family: 'Lato';
src: url('/static/fonts/Lato.woff2') format('woff2');
font-weight: 400;
}
h1 {
font-family: Lato, Arial, sans-serif;
}
我们现在要做的就是使用一个系统字体作为 Lato
的备用字体,比如 Arial
,然后在相应的 @font-face
规则中使用 size-adjust
描述符:
/* Web Fonts */
@font-face {
font-family: 'Lato';
src: url('./fonts/Lato.woff2') format('woff2');
font-weight: 400;
}
/* 使用系统字体作为 Web Fonts 的备用字体 */
@font-face {
font-family: "Lato-fallback";
size-adjust: 97.38%;
ascent-override: 99%;
src: local("Arial");
}
h1 {
font-family: Lato, Lato-fallback, sans-serif;
}
这意味着,使用了系统字体Arial
(系统字体可以直接使用,不需要额外下载),然后使用 size-adjust
和 ascent-override
让系统字体 Arial
更接近 Lato
字体(Web Fonts),并且该 @font-face
创建的字体 Lato-fallback
作为 Lato
的备用字体。注意,经过调整的 Lato-fallback
更接近 Lato
字体。 也就是说,从备用字体 Lato-fallback
切换到 Lato
(Web Fonts 字体加载完,会切换到 Lato
字体,当然,这也和 font-display
的值有关),造成的布局偏移要好很多。
@Malte Ubl 创建了一个工具Automatic font matching,可以在给定两个字体和一个支持这些新特性(ascent-override
、descent-override
、line-gap-override
和 size-adjust
)的浏览器的情况下自动计算这些属性的值:
Automatic font matching 是基于 @Monica Dinculescu 的 Font Style Matcher构建的。详细的可以阅读 @Malte Ubl 的 《More than you ever wanted to know about font loading on the web》一文。如果你的 Web Fonts 不是 Google Fonts,而又希望 Automatic Font Matching 工具帮助你计算字体之间的差异,那么你可以把该工具的代码下载下来,在代码中更换你自己想要的 Web Fonts。
也就是说,我们在使用 Web Fonts 时,可以把这些描述符放置到一个 @font-face
规则中,并且该 @font-face
引用一个系统字体来作为 Web Fonts 的备用字体。这样一来,就可以改善 font-display: swap
引起的布局偏移。这样就有一个两全其美的效果:内容可以尽快被看到,而且还可以得到 Web Fonts 带来的美化,同时还不会牺牲用户看内容的时间。
综合下来,我们在使用 Web Fonts 时,应该采用像下面这样使用,会给用户提供一个更好的体验,而且还能尽可能的改善 Web Fonts 带来的布局偏移(要完全根除 Web Fonts 带来的布局偏移是不可能的):
<head>
<!-- 如果 Web Fonts 和域名在同一个地方,preconnect 可以忽略 -->
<link rel="preconnect" href="https://g.alicdn.com" crossorigin />
<!-- Web Fonts 预加载,需要放置在页面关键渲染资源之后 -->
<link rel="preload" importance="high" href="https://g.alicdn.com/font/lota.woff2" as="font" type="font/woff2" crossorigin="anonymous">
<style>
/* 声明 Web Fonts */
@font-face {
font-display: swap;
font-family: lota;
font-style: normal;
font-weight: normal;
src: url('https://g.alicdn.com/font/lota.woff2') format('woff2');
}
/* 使用系统字体作为 Web Fonts 备用字体,并且使用 FontDrop 给的参数来覆盖 */
@font-face {
font-family: lota-fallback;
font-style: normal;
font-weight: normal;
src: local('Arial'); /* 使用系统的 Arial 作为备用字体 */
--unitsPerEm: 2000;
--lineGap: 0;
--descender: -426;
--ascender: 1974;
ascent-override: calc(var(--ascender) / var(--unitsPerEm) * 100%);
descent-override: calc(var(--descender) / var(--unitsPerEm) * 100%);
line-gap-override: calc(var(--lineGap) / var(--unitsPerEm) * 100%);
size-adjust: 107.6%; /* 有可能需要手动调整 size-adjust 值*/
}
/* 运用 Web Fonts*/
.font {
font-family: lota, lota-fallback, sans-serif;
}
</style>
</head>
**特别声明:
size-adjust
对应字体哪个参数,我也还没整明白,现在是手动调整这个属性的值。另外,size-adjust
不等同于text-size-adjust
和font-adjust-size
两属性,大家千万别混淆!**
有关于这方面更详细的介绍,可以阅读下面几篇文章:
- CSS size-adjust for @font-face
- A New Way To Reduce Font Loading Impact: CSS Font Descriptors
- More than you ever wanted to know about font loading on the web
一些可用于排版的 CSS 特性
前面我们提到过,Web Fonts 可以设计成可变字体,可变字体除了让字体文件体积变小之外,还可以使用一些 CSS 的属性来控制 Web Fonts 在浏览器上渲染效果。除此之外,CSS 还提供了一些用于排版方面的新特性(不是用在 @font-face
规则内)。这里再花简短的篇幅来聊一下这些属性。
间距(Spacing)和字距(Kerning)
在字体文件中,有两种设置可以定义字符之间的空间。在 CSS 中可以使用 letter-spacing
和 font-kerning
来控制:
letter-spacing
:用于设置文本字符的间距表现(每个字符左右两边的边距)font-kerning
:设置是否使用字体中存储的字距信息(两个字符之间的间距)
在字体中,间距(Spacing)是不能关闭的,否则渲染引擎(浏览器)在渲染文本时就不知道发何处理字符的间距;字距(Kerning)在浏览器中默认是关闭的,需要使用 CSS 的 font-kerning
来打开。
Kerning(字距)定义了字母的分布情况。对于良好地规定了字距的字体,字距特性使得字母分布更为统一,阅读体验更佳。
letter-spacing
大家非常熟悉,设置一个带有单位的正负值即可,比如 -.1px
;font-kerning
的使用也不复杂:
p {
font-feature-settings: "kern" 1;
font-kerning: normal;
}
OpenType 字体的高级功能
OpenType 功能很强大,它们为控制字体开启了大量的可能性,而不必为获得相同的效果提供多个字体文件。
前面所说的可变字体就是属于 OpenType的,该字体所支持的功能都是由字体设计者决定的,而且 是所有的字体都支持相同的功能。在 CSS 中除了 font-variation-settings
属性用于控制可变字体之外,还可以使用 font-feature-settings
属性用来控制 OpenType 字体中的高级印刷功能。比如:
p {
font-feature-settings: "onum" 1, "pnum" 1, "kern" 1, "ss01" 1;
}
示例代码中的 "onum"
(old style figures)、"pnum"
(proportional numerals)、"kern"
(kerning) 和 "ss01"
(stylistic sets) 代表着 OpenType 字体中不同的功能。有关于这些参数更详细的描述,可以阅读《font-feature-settings》:
Web开发者应该尽可能的使用类似
font-variant
这样的短标记属性或者相关的速记标识属性等, 类似font-variant-ligatures
,font-variant-caps
,font-variant-east-asian
,font-variant-alternates
,font-variant-numeric
或font-variant-position
。该属性是一个比较偏底层的功能接口,用于解决由于没有其他方法去访问和设置OpenType字体某些特性而无法解决一些特殊功能需求。特别需要注意的是,该CSS属性不应该用来开启大写字母转换。
字体平滑化
虽然字体文件中包含的提示信息在 MacOS 上大多数都被忽略了,但特定的浏览器对字体渲染提供了一些额外的控制。
html {
-webkit-font-smoothing: antialiased; /* Chrome, Safari */
-moz-osx-font-smoothing: grayscale; /* Firefox */
}
使用 font-smoothing
可以使 MacOS 和 iOS 上文本渲染更清楚、更细(Thin)。但这也可能导致渲染问题,特别是你已经使用了一种字更细(Thin)字体或font-weight
(字重)。antialiased
和 grayscale
主要用于平衡在深色背景上使用浅色文字时的字体渲染。
注意,该属性已从 CSS 规范中移除,但在一些阅读类的网站上还是可能看到该属性:
优化可读性
我在 Medium 网站上看到 <body>
元素上设置了一个 text-renderng: optimizeLegibility
样式规则:
text-rendering
定义浏览器渲染引擎如何渲染字体。浏览器会在速度、清晰度、几何精度之间进行权衡:
optimizeSpeed
:浏览器在绘制文本时将着重考虑渲染速度,而不是易读性和几何精度。它会使字间距和连字无效。optimizeLegibility
:浏览器在绘制文本时将着重考虑易读性,而不是渲染速度和几何精度。它会使字间距和连字有效。该属性值在移动设备上会造成比较明显的性能问题。geometricPrecision
:浏览器在绘制文本时将着重考虑几何精度, 而不是渲染速度和易读性。字体的某些方面—比如字间距—不再线性缩放,所以该值可以使使用某些字体的文本看起来不错。
如果你追求的是页面性能,那么
text-rendering
就慎用,甚至是不要使用。特别是在文本较多的地方。
字体性能的未来
很明显,字体性能还不是一个已经解决的问题。值得庆幸的是,在 W3C Web Fonts 工作组(Web Fonts Working Group)中,有一项工作正在进行,以改善这个问题。其中一项建议是渐进式字体丰富化,这是一个增量字体加载的概念,它允许浏览器准确地请求它们渲染文本所需的字符,有可能极大地减少初始字体文件的大小,从而提高渲染速度。
谷歌提供了一个关于这个增量传输概念的演示,如果你对这个概念感兴趣的话,可以深入阅读 @Jason Pamental的《Progressive Font Enrichment: reinventing web font performance》博文。
小结
布局偏移对用户体验不好,而且很难解决,特别是使用 Web Fonts 造成的布局偏移更难解决。虽然使用 font-display: optional
可以避免 Web Fonts 带来的布局偏移,但用户会在极短时间内看不到任何文本,甚至是字体加载完成也有可能看不到 Web Fonts 渲染的效果。而使用 font-display: swap
可以正常使用 Web Fonts 渲染文本,但会造成布局偏移。虽然 CSS 的新特性(ascent-override
、descent-override
、line-gap-override
和 size-adjust
)可以减少 Web Fonts 带来的布局偏移,但还是无法彻底根除。这似乎又进入到两难境地。或者说,就是和浏览器赛跑,即 试图在浏览器开始渲染文本之前加载完你的 Web Fonts。
通过优化你的字体文件,与浏览器赛跑是可能的。
- 使用
woff2
来最小化文件大小 - 自己服务器托管 Web Fonts
- 预加载关键字体(如果 Web Fonts 有多个,在一个主站上,使用
<link rel="preload">
不超过三个) - 使用
preconnect
,让用户提前连接到存放 Web Fonts 的域名 - 对所需字符进行字体子集
- 限制使用的重量变化的数量,不采用可变字体情况下,每个不同重量的字体需要一个字体文件
- 探索可变字体
- 尽可能使用系统字体
- 使用F-mods来减少字体交换的影响
如果你确认,在使用 Web Fonts 时不能使用 font-display: optional
控制浏览器加载字体和渲染字体策略(必须使用 font-display: swap
),那么就尽可能的使用 @font-face
规则加载一个系统字体作为该 Web Font 的备用字体,并且在该 @font-face
规则中配置 F-mods 描述符(ascent-override
、descent-override
、line-gap-override
)和 size-adjust
,尽可能让使用覆盖描述符修改后的系统字体更贴近 Web Fonts,更好的减少 Web Fonts 引起的布局偏移。