网页可变字体简介

一种可显著减小字体文件大小的新字体规范

在本文中,您将了解什么是可变字体、可变字体带来的好处,以及如何在工作中运用可变字体。首先,了解网页上的排版如何运作,以及可变字体带来了哪些创新。

浏览器兼容性

截至 2020 年 5 月,大多数浏览器都支持可变字体。请参阅可以使用可变字体吗?后备字体

简介

开发者经常会混用“字体”和“字样”这两个术语。 不过,字体的概念与字号有所不同:字体是可用于多种不同排版技术的底层视觉设计,而字号是其中一种实现方式,以数字文件格式呈现。换句话说,字形是您看到的,而字体是您使用的。

另一个经常被忽略的概念是样式与字族之间的区别。样式是指一种特定的字体,例如粗斜体,而字体系列是指完整的样式集。

在可变字体出现之前,每种样式都以单独的字体文件实现。借助可变字体,所有样式都可以包含在一个文件中。

Roboto 系列的样本组成和不同样式的列表。
左侧:Roboto 字体系列的样本。右侧:系列中的命名样式。

设计者和开发者面临的挑战

设计师在创建印刷项目时会面临一些限制,例如页面布局的实际尺寸、可使用的颜色数量(由将要使用的印刷机的类型决定)等。但他们可以根据需要使用任意数量的字体样式。这意味着,印刷媒体的排版通常丰富而精致,因此阅读体验非常愉悦。回想一下您上次浏览一本精彩杂志时的感受。

与平面设计师相比,网页设计师和开发者面临的限制有所不同,其中一个重要限制是设计相关的带宽费用。这一直是实现更丰富的排版体验的障碍,因为它们需要付出一定的代价。使用传统 Web 字体时,设计中使用的每种样式都需要用户下载单独的字体文件,这会增加延迟时间和网页渲染时间。仅包含常规和粗体样式及其对应的斜体样式,就可能需要 500 KB 或更多的字体数据。这甚至是在我们处理字体呈现方式、需要使用的回退模式或不良副作用(例如 FOIT 和 FOUT)之前。

许多字体系列提供更广泛的样式,从极细到极粗的字重、窄和宽的宽度、各种样式细节,甚至还有针对特定尺寸的设计(针对大号或小号文本尺寸进行了优化)。由于您必须为每种样式(或样式组合)加载新的字体文件,因此许多 Web 开发者选择不使用这些功能,从而降低了用户的阅读体验。

可变字体剖析

可变字体通过将样式打包到单个文件中来解决这些难题。

这种方法首先从一种中心或“默认”样式(通常是“常规”样式)开始,这是一种直立的罗马字体设计,具有最典型的粗细和宽度,最适合纯文本。然后,将此样式与连续范围内的其他样式相关联,称为“轴”。最常见的轴是粗细,它可以将默认样式与粗体样式连接起来。任何单个样式都可以沿轴定位,称为可变字体的“实例”。有些实例由字体开发者命名,例如粗细轴位置 600 称为“半粗”。

可变字体 Roboto Flex粗细轴有三种样式。常规样式位于中间,轴的两端是两种样式,一种较浅,另一种较深。在这些实例之间,您可以从 900 个实例中进行选择:

以不同粗细显示的字母“A”。
Roboto 字体的粗细轴的图示解剖。

字体开发者可以提供一组不同的轴。您可以将它们合并,因为它们都共享相同的默认样式。Roboto 在宽度轴上有三种样式:常规样式位于轴的中心,较窄和较宽的两种样式分别位于轴的两端。这些变量提供常规样式的全部宽度,并与粗细轴结合使用,以提供各种粗细的全部宽度。

宽度和粗细随机组合的 Roboto Flex

这意味着有数千种样式!这可能看起来有点过分,但这种多样化的字体样式可以显著提升阅读体验的质量。而且,如果不会降低性能,Web 开发者可以根据自己的设计需要,使用任意数量的样式。

斜体

可变字体中斜体的处理方式很有趣,因为有两种不同的方法。Helvetica 或 Roboto 等字体具有插值兼容的轮廓,因此其罗马体和斜体样式可以在两者之间进行插值,并且可以使用倾斜轴从罗马体变为斜体。

其他字体(例如 Garamond、Baskerville 或 Bodoni)的罗马字形和斜体字形轮廓不兼容插值。例如,通常用于定义罗马小写字母“n”的轮廓与用于定义斜体小写字母“n”的轮廓不匹配。斜体轴不是在一种轮廓与另一种轮廓之间进行插值,而是从罗马轮廓切换到斜体轮廓。

Amstelvar 字体的粗细轴示例。
Amstelvar 的斜体“n”轮廓(12 点、常规粗细、正常宽度)和正体“n”轮廓。图片由 Font Bureau 的字体设计师兼排版师 David Berlow 提供。

切换到斜体后,用户可用的轴应与罗马体相同,字符集也应相同。

此外,您还可以看到单个字形的字形替换功能,并可在可变字体的设计空间中的任何位置使用该功能。例如,带有两条竖线的美元符号设计在大字号下效果最好,但在小字号下,只有一条竖线的设计效果更好。如果用于渲染字形的像素较少,双条设计可能会难以辨认。为了解决这个问题,与斜体轴类似,字形设计师可以在光学尺寸轴上决定一个点,在该点处发生一个字形替换另一个字形的情况。

总而言之,在轮廓允许的情况下,字体设计师可以在多维设计空间中创建在各种样式之间插值的字体。这样一来,您就可以精细控制排版,并获得极大的灵活性。

轴定义

有五种已注册的轴,用于控制字体已知的、可预测的特征:粗细、宽度、光学尺寸、斜度和斜体。除这些轴外,字体还可以包含自定义轴。这些变量可以控制字体设计师希望控制的字体的任何设计方面:衬线的大小、花体的长度、升部的高度或 i 上方圆点的大小。

即使轴可以控制同一功能,它们也可能使用不同的值。例如,在 Oswald 和 Hepta Slab 可变字体中,只有一个轴(粗细)可用,但范围不同:Oswald 的范围与升级为可变字体之前相同,为 200 到 700,但 Hepta Slab 的范围从极细的 1 一直延伸到 900。

这五个注册轴具有 4 个字符的小写标记,用于在 CSS 中设置它们的值:

轴名称和 CSS 值
权重 wght
宽度 wdth
倾斜 slnt
光学尺寸 opsz
斜体 ital

由于字体开发者会定义可变字体中可用的轴以及这些轴可以具有的值,因此务必要了解每种字体提供的功能。字体文档应提供此信息,或者您可以使用 Wakamai Fondue 等工具检查字体。

使用场景和优势

设置轴值取决于个人喜好和排版最佳实践。任何新技术都可能被滥用,而过于艺术化或探索性的设置也可能会降低实际文本的可读性。对于标题,探索不同的轴来创作出色的艺术设计令人兴奋,但对于正文,这可能会导致文字难以辨认。

精彩的表达

Mandy Michael 提供的草地示例
在 CodePen 中查看此示例。

这个出色的艺术表达示例是 Mandy Michael 对字体 Decovar 的探索。

动画

Zycon 字体,由 Font Bureau 的字体设计师兼排版师 David Berlow 专门为动画设计。

您还可以探索如何使用可变字体为字符添加动画效果。 上方的视频展示了 Zycon 字体的不同轴。请参阅 Axis Praxis 上的实时动画示例

Anicons 是世界上首款基于 Material Design 图标的彩色动画图标字体。Anicons 是一项实验,它将两种前沿的字体技术(即可变字体和彩色字体)相结合。

Anicon 的彩色图标字体中的一些悬停动画示例

Finesse

Amstelvar 使用少量 XTRA 以相反方向调整,使文字宽度均匀

Roboto FlexAmstelvar 提供了一组“参数化轴”。在这些轴上,字母被分解为 4 个基本形状方面:黑色或正形状、白色或负形状,以及 x 和 y 维度。与原色可以与任何其他颜色混合以进行调整的方式类似,这 4 个方面可用于微调任何其他轴。

在 Amstelvar 中,XTRA 轴可让您调整千分比“白色”值,如上例所示。通过在相反方向上使用少量 XTRA,可以使字词宽度保持一致。

CSS 中的可变字体

加载可变字体文件

可变字体通过与传统静态 Web 字体相同的 @font-face 机制加载,但具有两项新增强功能:

@font-face {
    font-family: 'Roboto Flex';
    src: url('RobotoFlex-VF.woff2') format('woff2-variations');
    src: url('RobotoFlex-VF.woff2') format('woff2') tech('variations');
    font-weight: 100 1000;
    font-stretch: 25% 151%;
}

1. 来源格式:我们不希望浏览器在不支持可变字体的情况下下载字体,因此添加了 formattech 说明:一次采用未来语法 (format('woff2') tech('variations')),一次采用已弃用但受浏览器支持的语法 (format('woff2-variations'))。如果浏览器支持可变字体和即将推出的语法,则会使用第一个声明。如果支持可变字体和当前语法,则会使用第二种声明。它们都指向同一字体文件。

2. 样式范围:您会注意到,我们为 font-weightfont-stretch 提供了两个值。现在,我们不再告知浏览器相应字体提供哪些具体粗细(例如 font-weight: 500;),而是告知浏览器相应字体支持的粗细范围。对于 Roboto Flex,粗细轴的范围为 100 到 1000,CSS 直接将轴范围映射到 font-weight 样式属性。通过在 @font-face 中指定范围,任何超出此范围的值都将“封顶”为最接近的有效值。宽度轴范围以相同方式映射到 font-stretch 属性。

如果您使用的是 Google Fonts API,则无需担心这些问题。CSS 不仅会包含正确的来源格式和范围,Google Fonts 还会发送静态后备字体,以防可变字体不受支持。

使用粗细和宽度

目前,您可以通过 CSS 可靠设置的轴是 font-weight 中的 wght 轴和 font-stretch 中的 wdth 轴。

传统上,您会将 font-weight 设置为关键字(lightbold),或设置为介于 100 到 900 之间的数值(以 100 为步长)。借助可变字体,您可以设置字体宽度范围内的任何值:

.kinda-light {
  font-weight: 125;
}

.super-heavy {
  font-weight: 1000;
}
Roboto Flex 的粗细轴从最小值更改为最大值。

同样,我们可以使用关键字(condensedultra-expanded)或百分比值来设置 font-stretch

.kinda-narrow {
  font-stretch: 33.3%;
}

.super-wide {
  font-stretch: 151%;
}
Roboto Flex 的宽度轴从最小值更改为最大值。

使用斜体和倾斜字体

ital 轴适用于同时包含常规样式和斜体样式的字体。此轴旨在作为开关:值 0 表示关闭,将显示常规样式;值 1 表示开启,将显示斜体。与其他轴不同,此轴没有过渡效果。值为 0.5 时,不会出现“半斜体”效果。

slnt 轴与斜体不同,因为它不是一种新的样式,而只是倾斜常规样式。默认值为 0,表示默认的正体字母形状。Roboto Flex 的最大倾斜角度为 -10 度,这意味着字母在从 0 度到 -10 度时会向右倾斜。

通过 font-style 属性设置这些轴会很直观,但截至 2020 年 4 月,如何准确地执行此操作仍在研究中。因此,目前您应将这些视为自定义轴,并通过 font-variation-settings 进行设置:

i, em, .italic {
    /* Should be font-style: italic; */
    font-variation-settings: 'ital' 1;
}

.slanted {
    /* Should be font-style: oblique 10deg; */
    font-variation-settings: 'slnt' 10;
}
Roboto Flex 的倾斜轴从最小值更改为最大值。

使用光学尺寸

字体的渲染尺寸可以非常小(12 像素的脚注),也可以非常大(80 像素的标题)。字体可以通过更改其字母形状来响应这些大小变化,从而更好地适应其大小。小尺寸可能更适合没有精细细节的图片,而大尺寸可能更适合包含更多细节和更细笔画的图片。

以不同光学尺寸显示的字母“a”。
Roboto Flex 中不同像素大小的字母“a”,然后缩放到相同大小,显示设计上的差异。 在 Codepen 上亲自尝试一下

已为此轴引入新的 CSS 属性:font-optical-sizing。默认情况下,它设置为 auto,这会使浏览器根据 font-size 设置轴值。这意味着浏览器会自动选择最佳的光学尺寸,但如果您想关闭此功能,可以将 font-optical-sizing 设置为 none

如果您有意要使用与字号不匹配的光学尺寸,还可以为 opsz 轴设置自定义值。以下 CSS 会使文本以大尺寸显示,但光学尺寸与以 8pt 打印时的尺寸相同:

.small-yet-large {
  font-size: 100px;
  font-variation-settings: 'opsz' 8;
}

使用自定义轴

与已注册的轴不同,自定义轴不会映射到现有的 CSS 属性,因此您始终必须通过 font-variation-settings 设置它们。自定义轴的标记始终采用大写形式,以便与注册轴区分开来。

Roboto Flex 提供了一些自定义轴,其中最重要的是粗细轴 (GRAD)。粗细轴很有趣,因为它可以在不改变字体宽度的情况下改变字体的粗细,因此换行符不会改变。通过调整 Grade 轴,您可以避免被迫调整影响整体宽度的 Weight 轴,然后再调整影响整体粗细的 Width 轴。

Roboto Flex 的等级轴从最小值更改为最大值。

由于 GRAD 是一个自定义轴,在 Roboto Flex 中的范围为 -200 到 150。 我们需要使用 font-variation-settings 来解决此问题:

.grade-light {
    font-variation-settings: `GRAD` -200;
}

.grade-normal {
    font-variation-settings: `GRAD` 0;
}

.grade-heavy {
    font-variation-settings: `GRAD` 150;
}

Google Fonts 中的可变字体

Google Fonts 已通过可变字体扩大了其目录,并且还在定期添加新字体。 该界面目前旨在从字体中选择单个实例:您选择所需的变体,点击“选择此样式”,然后该变体会被添加到从 Google Fonts 获取 CSS 和字体的 <link> 元素中。

如需使用所有可用的轴或值范围,您必须手动构建 Google Fonts API 的网址。可变字体概览会列出所有轴和值。

Google 可变字体链接工具还可以为您提供完整可变字体的最新网址。

font-variation-settings 继承

虽然所有注册的轴很快都将通过现有的 CSS 属性得到支持,但目前您可能需要依赖 font-variation-settings 作为后备方案。如果您的字体具有自定义轴,您还需要 font-variation-settings

不过,font-variation-settings 有一个需要注意的小问题。所有未明确设置的属性都会自动重置为默认值。之前设置的值不会被继承!这意味着以下功能将无法正常运行:

<span class="slanted grade-light">
    I should be slanted and have a light grade
</span>

首先,浏览器将应用 .slanted 类中的 font-variation-settings: 'slnt' 10。然后,它将应用 .grade-light 类中的 font-variation-settings: 'GRAD' -200。不过,这会将 slnt 重置回默认值 0!结果将是浅色文本,但不会倾斜。

幸运的是,我们可以使用 CSS 变量来解决此问题:

/* Set the default values */
:root {
    --slnt: 0;
    --GRAD: 0;
}

/* Change value for these elements and their children */
.slanted {
    --slnt: 10;
}

.grade-light {
    --grad: -200;
}

.grade-normal {
    --grad: 0;
}

.grade-heavy {
    --grad: 150;
}

/* Apply whatever value is kept in the CSS variables */
.slanted,
.grade-light,
.grade-normal,
.grade-heavy {
    font-variation-settings: 'slnt' var(--slnt), 'GRAD' var(--GRAD);
}

CSS 变量会级联,因此如果某个元素(或其某个父元素)已将 slnt 设置为 10,即使您将 GRAD 设置为其他值,该元素也会保留该值。如需详细了解此技巧,请参阅修复可变字体继承问题

请注意,CSS 变量的动画效果无法实现(这是设计使然),因此以下代码无法正常运行:

@keyframes width-animation {
   from { --wdth: 25; }
   to   { --wdth: 151; }
}

这些动画必须直接在 font-variation-settings 上进行。

效果提升

借助 OpenType 可变字体,我们可以将一个字体系列的多种变体存储到单个字体文件中。Monotype 通过组合 12 种输入字体来生成 8 种字重,涵盖 3 种宽度,包括斜体和罗马体样式,从而进行了一项实验。将 48 种单独的字体存储在一个可变字体文件中,这意味着文件大小减少了 88%

不过,如果您只使用一种字体(例如 Roboto Regular),那么即使改用具有多个轴的可变字体,字体大小也可能不会有明显变化。与往常一样,这取决于您的使用场景。

另一方面,在设置之间为字体添加动画效果可能会导致性能问题。虽然随着浏览器中对可变字体的支持日趋成熟,这种情况会有所改善,但目前可以通过仅对屏幕上显示的字体进行动画处理来在一定程度上缓解此问题。Dinamo 提供的这段实用代码段可在类为 vf-animation 的元素不在屏幕上时暂停这些元素中的动画:

var observer = new IntersectionObserver(function(entries, observer) {
  entries.forEach(function(entry) {
    // Pause/Play the animation
    if (entry.isIntersecting) entry.target.style.animationPlayState = "running"
    else entry.target.style.animationPlayState = "paused"
  });
});

var variableTexts = document.querySelectorAll(".vf-animation");
variableTexts.forEach(function(el) { observer.observe(el); });

如果您的字体会响应用户互动,最好对输入事件进行节流或去抖。这样可防止浏览器渲染与上一个实例相比变化很小的人眼无法察觉的变量字体实例。

如果您使用的是 Google Fonts,最好预先连接https://fonts.gstatic.com(Google 字体的托管网域)。这样可确保浏览器在 CSS 中遇到字体时,能够尽早知道从何处获取字体:

<link rel="preconnect" href="https://fonts.gstatic.com" />

此技巧也适用于其他 CDN:您越早让浏览器设置网络连接,浏览器就能越早下载字体。

如需了解有关加载 Google 字体的更多性能提示,请参阅最快的 Google 字体

后备和浏览器支持

所有新式浏览器都支持可变字体。如果您需要支持旧版浏览器,可以选择使用静态字体构建网站,并将可变字体用作渐进式增强功能:

/* Set up Roboto for old browsers, only regular + bold */
@supports not (font-variation-settings: normal) {
  @font-face {
    font-family: Roboto;
    src: url('Roboto-Regular.woff2');
    font-weight: normal;
  }

  @font-face {
    font-family: Roboto;
    src: url('Roboto-Bold.woff2');
    font-weight: bold;
  }

  body {
    font-family: Roboto;
  }

  .super-bold {
    font-weight: bold;
  }
}

/* Set up Roboto for modern browsers, all weights */
@supports (font-variation-settings: normal) {
  @font-face {
    font-family: 'Roboto';
    src: url('RobotoFlex-VF.woff2') format('woff2 supports variations'),
         url('RobotoFlex-VF.woff2') format('woff2-variations');
    font-weight: 100 1000;
    font-stretch: 25% 151%;
  }

  .super-bold {
    font-weight: 1000;
  }
}

对于旧版浏览器,具有 .super-bold 类的文本将以常规粗体呈现,因为这是我们唯一可用的粗体字体。如果支持可变字体,我们实际上可以使用最重的字重 1000。

Internet Explorer 不支持 @supports 规则,因此该浏览器不会显示任何样式。如果这是一个问题,您始终可以使用旧式技巧来定位相关旧版浏览器。

如果您使用的是 Google Fonts API,它会负责为访问者的浏览器加载合适的字体。假设您请求粗细范围为 200 到 700 的 Oswald 字体,如下所示:

<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">

能够处理可变字体的现代浏览器将获得可变字体,并且可以使用 200 到 700 之间的所有字重。旧版浏览器将为每个字重提供单独的静态字体。在这种情况下,这意味着他们将下载 6 个字体文件:一个用于粗细 200,一个用于粗细 300,依此类推。

谢谢

本文的完成离不开以下人员的帮助: