网页可变字体简介

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

在本文中,您将了解什么是可变字体、可变字体的优势,以及如何在工作中使用可变字体。首先,了解排版在 Web 上的工作原理,以及可变字体带来的创新。

浏览器兼容性

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

简介

开发者通常会将“字体”和“字体样式”这两个术语互换使用。不过,二者之间还是有区别:字体是可在许多不同的排版技术中存在的底层视觉设计,而字体是这些实现之一,采用数字文件格式。换句话说,字体是您看到的,而字体是您使用的

另一个经常被忽视的概念是样式和系列之间的区别。样式是单一且特定的字体(例如粗体斜体),字体系列是完整的样式集。

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

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

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

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

Web 设计师和开发者面临的约束条件与平面设计师不同,其中一个重要约束条件就是设计的相关带宽费用。这一直是实现更丰富排版体验的难点,因为这需要付出代价。使用传统的 Web 字体时,设计中使用的每种样式都需要用户下载单独的字体文件,这会增加延迟时间和页面呈现时间。仅包含“普通”和“粗体”样式以及其斜体对应项,字体数据量就可能达到 500 KB 或更多。这还只是在我们处理字体呈现方式、需要使用的后备模式或 FOIT 和 FOUT 等不良副作用之前。

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

可变字体剖析

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

具体方法是从中性或“默认”样式(通常为“Regular”)开始,这种样式采用直立的罗马设计,具有最典型的粗细和宽度,最适合用于普通文本。然后,将其与连续范围内的其他样式(称为“轴”)相关联。最常见的轴是粗细,它可以将默认样式与粗体样式相关联。任何单独的样式都可以沿轴线放置,并且称为可变字体的“实例”。有些实例由字体开发者命名,例如粗细轴位置 600 称为“粗体”。

可变字体 Roboto FlexWeight 轴有三种样式。“常规”样式位于中间,轴的两端各有一种样式,一种较浅,一种较深。您可以从 900 个实例中进行选择:

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

字体开发者可以提供一组不同的轴。您可以将它们组合使用,因为它们都采用相同的默认样式。Roboto 在“宽度”轴上有三种样式:常规样式位于轴的中心,两种样式(较窄和较宽)位于两端。这些值提供了“Regular”样式的所有宽度,并与“Weight”轴结合使用,以提供每种粗细的所有宽度。

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

也就是说,有数千种样式!这可能看起来过于夸张,但这种多样化的排版样式可以显著提升阅读体验的质量。如果不会降低性能,Web 开发者可以根据自己的设计,使用少量或任意数量的样式。

斜体

可变字体中处理斜体的方式很有趣,因为有两种不同的方法。Helvetica 或 Roboto 等字体具有可插值的轮廓,因此其正体和斜体样式之间可以插值,并且可以使用倾斜度轴从正体转换为斜体。

其他字体(例如 Garamond、Baskerville 或 Bodoni)的罗马字体和斜体字符轮廓不兼容插值。例如,通常用于定义罗马小写字母“n”的轮廓与用于定义斜体小写字母“n”的轮廓不匹配。Italic 轴会从罗马风格轮廓切换到斜体轮廓,而不是将一个轮廓插值到另一个轮廓。

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

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

字形替换功能还适用于单个字形,可在可变字体的设计空间中的任何位置使用。例如,在较大的点大小下,带有两个垂直条的美元符号设计最适合,但在较小的点大小下,仅带有一个条的设计更好。如果用于渲染字体的像素较少,双横线设计可能会难以辨认。为了解决此问题,与“Italic”轴类似,在由排版设计师决定的某个点,Optical Size 轴上可以发生一个字形替换另一个字形的情况。

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

轴定义

有五个已注册的轴,用于控制字体的已知可预测特征:粗细、宽度、光学尺寸、斜度和斜体。除此之外,字体还可以包含自定义轴。这些参数可以控制字体设计师想要的字体的任何设计方面:衬线的大小、装饰线的长度、上衬线的高度或 i 上的圆点的大小。

即使轴可以控制同一项,但它们可能使用不同的值。例如,在 Oswald 和 Hepta Slab 可变字体中,只有一个轴,即粗细,但范围不同 - Oswald 的范围与升级为可变字体之前的范围相同,即 200 到 700,但 Hepta Slab 的极细粗细为 1,最高可达 900。

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

轴名称和 CSS 值
重量 wght
宽度 wdth
斜体 slnt
Optical Size 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 提供了一些自定义轴,其中最重要的是 Grade (GRAD)。Grade 轴很有趣,因为它会更改字体的粗细,而不会更改宽度,因此换行符不会发生变化。通过调整“Grade”轴,您可以避免被迫调整影响整体宽度的“Weight”轴,然后再调整影响整体粗细的“Width”轴。

Roboto Flex 的“Grade”轴从最小值更改为最大值。

由于 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 已在其目录中添加了可变字体,并会定期添加新字体。该界面目前旨在从字体中选择单个实例:您选择所需的变体,点击“Select this style”(选择此样式),该变体将添加到从 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 种输入字体,在三种宽度和斜体和罗马风格中生成了八种粗细。在单个可变字体文件中存储 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 Fonts 的更多性能提示,请参阅最快的 Google Fonts

回退和浏览器支持

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

/* 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,该 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,依此类推。

谢谢

以下人员对本文的撰写提供了帮助: