网页可变字体简介

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

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

浏览器兼容性

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

简介

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

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

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

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

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

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

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

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

可变字体剖析

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

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

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

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

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

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 的草地示例

上面展示了一个极佳的艺术表现示例,即 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 中指定范围,此范围之外的任何值都将被“上限”为最接近的有效值。Width 轴范围以相同的方式映射到 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 已在其目录中添加了可变字体,并会定期添加新字体。该界面目前旨在从字体中选择单个实例:您选择所需的变体,点击“选择此样式”,该变体将添加到从 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,依此类推。

谢谢

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

主打图片由 Unsplash 用户 Bruno Martins 提供。