网页可变字体简介

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

Dave Crossland
Dave Crossland
Mustafa Kurtuldu
Mustafa Kurtuldu
Roel Nieskens
Roel Nieskens
Thomas Steiner
Thomas Steiner

在本文中,我们将介绍什么是可变字体、它们提供的优势,以及如何在工作中使用可变字体。首先,让我们回顾一下排版在 Web 上的工作原理,以及可变字体带来的创新。

浏览器兼容性

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

简介

开发者经常将“字体”和“字体”这两个术语互换使用。但两者之间有区别:字体是可以存在于许多不同排版技术中的底层视觉设计,而字体是数字文件格式的其中一种实现方式。换言之,字体是您看到的内容,字体是您使用的内容。

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

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

Roboto 系列不同样式的样本构成和清单
左图:Roboto 字体系列的示例。右图:系列中已命名的样式。

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

在设计印刷项目时,设计人员会面临一些约束,例如页面布局的物理尺寸、可以使用的颜色数量(取决于要使用的印刷机类型)等。但他们可以根据需要使用任意数量的字体样式。这意味着,平面媒体的排版通常丰富而复杂,因此能够提供真正令人愉悦的阅读体验。回想一下您上次阅读一本精彩杂志时开心的事。

网页设计师和开发者与平面设计人员有着不同的限制,其中一个重要的方面是设计的相关带宽成本。这已成为提供更丰富的排版体验的症结,因为这样做需要付出代价。对于传统网页字体,我们设计中使用的每种样式都要求用户下载单独的字体文件,这会增加延迟和网页渲染时间。如果仅包含“常规”和“粗体”样式(及其对应的斜体样式),则相当于 500 KB 或更多的字体数据。这甚至在我们涉及字体渲染方式、需要使用的回退模式或不良副作用(如 FOIT 和 FOUT)之前就已发生。

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

可变字体详解

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

具体方法是从中间样式或“默认”样式(通常是“常规”样式��开始,这是一种竖直的罗马样式,粗细和宽度最为典型,最适合纯文本。然后将其连接到一个连续范围内的其他样式,即“轴”。最常见的轴是粗细,它可以将默认样式关联到粗体样式。任何单独的样式都可以沿着轴定位,称为可变字体的“实例”。某些实例由字体开发者命名,例如,粗细轴位置 600 称为 我们会为 美英文字体开发者命名。

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

以不同粗细显示的字母“A”
上图:Robo 字体的权重轴剖析图。

字体开发者可以提供一组不同的轴。您可以结合使用这两种样式,因为它们采用相同的默认样式。Roboto 在宽度轴上具有三种样式:常规样式位于轴的中心,两端是较窄和较宽的两种样式。它们可提供常规样式的所有宽度,并与权重轴合并,从而提供每个权重的所有宽度。

随机组合宽度和重量的 Roboto Flex

这意味着有数千种样式!这看起来似乎有点大,但这种字体样式的多样性可以显著提高阅读体验的质量。而且,如果不影响性能,Web 开发者可以根据需要使用任意数量的样式,这取决于他们的设计。

斜体

在可变字体中处理斜体的方式很有趣,因为有两种不同的方法。Helvetica 或 Roboto 等字体的轮廓与插值兼容,因此,可以在二者之间插入罗马和斜体样式,倾斜轴可用于将罗马字体转换为斜体。

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

Amstelvar 字体的权重轴示例
Amstelvar 的“n”边的斜体(12 磅,常规粗细,正常宽度)和罗马文。图片由 Font Bureau 字体设计师和排版师 David Berlow 提供。

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

字形替换功能也可以用于单个字形,并在可变字体的设计空间中的任何位置使用。例如,具有两个竖条的美元符号设计在点较大时效果最佳,但在较小的点大小下,只有一个竖条的设计效果会更好。当用于渲染字形的像素较少时,双条形的设计可能会变得难以辨认。为解决此问题,与斜体轴非常相似,字体设计人员确定的位置沿 Optical Size 轴可以用一个字形替换另一个字形。

总而言之,在轮廓可以实现的情况下,字体设计人员可以创建在多维设计空间中的各种样式之间进行插值处理的字体。这样一来,您就可以精细地控制字体排版,并获得更强大的功能。

轴定义

共有五个注册轴,用于控制字体的已知可预测特征:粗细、宽度、光学大小、斜体和斜体。除此之外,字体还可以包含自定义轴。这些控件可以控制字体设计人员所需的字体的任何设计方面:衬线大小、水滑花纹的长度、上升线的高度或 i 上点的大小。

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

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

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

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

使用场景和优势

轴值设置取决于个人喜好,以及排版方面的最佳实践。任何新技术都有潜在被滥用的风险,过于艺术化或探索性的设置也可能会降低实际文本的易读性。对于游戏来说,探索不同的轴来打造出色的艺术设计是令人兴奋的,但对于正文而言,这可能会使文本难以辨认。

激动人心的表情

由 Mandy Michael 创作的 Grass 示例

上面展示了一个很好的艺术表现示例,探索了 Mandy Michael 的Decovar 字体。

您可以在此处查看上述示例的工作示例和源代码。

动画

Typeface Zycon,由 Font Bureau 字体设计师和排版师 David Berlow 专为动画设计。

还可以探索使用可变字体为字符添加���画效果。上面的示例展示了不同轴与字体 Zycon 配合使用的情况。请参阅 Axis Praxis 上的实时动画示例

Anicons 是世界上首个基于 Material Design 图标的动画彩色图标字体。Anicons 是一项实验性功能,结合了两种最先进的字体技术:可变字体和彩色字体。

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

精细度

Amstelvar 在相反方向使用少量 XTRA,使字词宽度均衡

Roboto FlexAmstelvar 提供了一组“参数轴”。在这些轴中,字母被分解为 4 个形态的基本组成部分:黑色或正形状、白色或负形,以及 x 和 y 维度。这 4 个方面可用于微调任何其他轴,就像将主色与任何���他颜色混合来调整颜色一样。

通过 Amstelvar 的 XTRA 轴,您可以调整千分比“白色”的值,如上所示。在相反方向使用少量 XTRA,字词宽度会均衡。

CSS 中的可变字体

加载可变字体文件

可变字体通过与传统静态网页字体相同的 @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,Weight 轴的范围为 100 到 1000,CSS 会直接将轴范围映射到 font-weight 样式属性。通过在 @font-face 中指定范围,系统会将此范围以外的任何值“限制”为最接近的有效值。宽度轴范围映射到 font-stretch 属性的方式相同。

如果您使用的是 Google Fonts API,这将完成所有操作。不仅 CSS 会包含正确的源格式和范围,Google Fonts 还会发送静态回退字体,以防可变字体不受支持。

使用权重和宽度

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

按照传统,您可以将 font-weight 设置为关键字(lightbold)或 100 到 900 之间的数值(按照 100 步中的说明)。使用可变字体时,您可以设置字体 Width 范围内的任何值:

.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 的倾斜轴从最小值更改为最大值。

使用光学尺寸

字体可以呈现得非常小(12px 脚注)或非常大(80px 的标题)。字体可以通过更改字母形状来适应这些大小变化,以更好地适应其大小。小尺寸若没有微小的细节,效果会更好,而较大的尺寸则可能更适合进行更详细的和更细的笔画。

以不同光学尺寸显示的字母“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 轴很有趣,因为它会在不更改宽度的情况下更改字体的粗细,因此换行符不会更改。使用“成绩”轴,您可以避免被迫对“权重”轴的更改影响整体宽度,然后再更改“宽度”轴会影响总体权重。

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 已扩充其目录,新增了可变字体,并会定期添加新的字体。该界面目前旨在从字体中选择单个实例:您选择所需的变体,点击“选择此样式”,该样式将被添加到 <link> 元素中,用于从 Google Fonts 中提取 CSS 和字体。

若要使用所有可用的轴或值的范围,您必须手动编写 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 可变字体,我们可将一个类型系列的多个变体存储到单个字体文件中。 单排版进行了一项实验,将 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:您越早让浏览器设置网络连接,它就能越快下载您的字体。

请参阅 The Fastest 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 类的文本将以常规粗体呈现,因为这是我们唯一可用的粗体字体。如果支持可变字体,我们实际上可以使用最大的粗细 1, 000。

Internet Explorer 不支持 @supports 规则,因此此浏览器不会显示任何样式。如果出现问题,您可以随时使用一种老式黑客手段来定位相关的旧版浏览器。

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

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

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

此致

只有在以下人员的帮助下,本文才能顺利完成:

主打图片:Bruno MartinsUnsplash 网站