网络上的可变字体简介

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

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

浏览器兼容性

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

简介

开发者经常互换使用术语“font”(字体)和“typeface”(字型)。但是,两者之间存在差异:字型是潜在的视觉设计,它可以存在于许多不同的排版技术中,而字体是这些实现方式之一,采用数字文件格式。换句话说,字型是您看到的,而字体是您使用的。

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

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

A specimen composition and list of different styles of the Roboto family
左图:Roboto 字型系列的样本。右图:字体系列中命名的样式。

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

当设计师创建印刷项目时,他们会面临一些限制,例如页面布局的物理尺寸、他们可以使用的颜色数量(这由将要使用的印刷机的类型决定)等等。但是他们可以使用任意多种字型样式。这意味着印刷媒体的排版通常丰富而精致,从而使阅读体验真正令人愉悦。想想您上次欣赏一本精美的杂志是什么时候。

Web 设计师和开发者面临的约束与印刷设计师不同,其中一个重要的约束是与我们的设计相关的带宽成本。这一直是更丰富的排版体验的症结所在,因为它们是有成本的。使用传统的 Web 字体,我们设计中使用的每种样式都需要用户下载单独的字体文件,这会增加延迟和页面渲染时间。仅包含常规和粗体样式及其斜体对应样式,字体数据就可能达到 500 KB 或更多。这甚至在我们处理字体如何渲染、我们需要使用的后备模式或不良副作用(例如 FOIT 和 FOUT)之前。

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

可变字体的结构

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

其工作原理是从中心或“默认”样式开始,通常是“常规”样式 - 一种直立的罗马体设计,具有最典型的字重和字宽,最适合纯文本。然后将其连接到连续范围内的其他样式,称为“轴”。最常见的轴是字重,它可以将默认样式连接到粗体样式。任何单个样式都可以沿着轴定位,并且称为可变字体的“实例”。某些实例由字体开发者命名,例如字重轴位置 600 称为半粗体。

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

The letter 'A' shown in different weights
上图:Roboto 字型的字重轴的图示结构。

字体开发者可以提供一组不同的轴。您可以将它们组合起来,因为它们都共享相同的默认样式。Roboto 在字宽轴中有三种样式:“常规”样式位于轴的中心,两端各有两种样式,一种较窄,另一种较宽。这些样式提供了“常规”字体的所有字宽,并与“字重”轴结合使用,为每种字重提供所有字宽。

Roboto Flex 在字宽和字重的随机组合中

这意味着有数千种样式!这看起来似乎有点过分,但是这种多样化的字体样式可以显著提高阅读体验的质量。而且,如果它没有性能损失,Web 开发者可以使用任意多种样式,这取决于他们的设计。

斜体

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

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

Example of the Weight Axes for the typeface Amstelvar
Amstelvar 的“n”轮廓,斜体(12 磅,常规字重,正常字宽)和罗马体。图片由字体设计师和排版师 David Berlow(Font Bureau)提供。

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

字形替换功能也可以用于单个字形,并用于可变字体设计空间中的任何位置。例如,带有两个竖线的美元符号设计在大磅值尺寸下效果最佳,但在较小磅值尺寸下,只有一个竖线的设计更好。当我们用于渲染字形的像素较少时,双竖线设计可能会变得难以辨认。为了解决这个问题,就像斜体轴一样,可以沿着光学尺寸轴在字体设计师决定的点处进行一个字形替换为另一个字形。

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

轴定义

有五个注册轴,它们控制字体的已知、可预测的特征:字重、字宽、光学尺寸、倾斜度和斜体。除此之外,字体还可以包含自定义轴。这些轴可以控制字体设计师希望的字体的任何设计方面:衬线的尺寸、花饰线的长度、上升部的高度或字母 i 上圆点的大小。

即使轴可以控制相同的特征,它们也可能使用不同的值。例如,在 Oswald 和 Hepta Slab 可变字体中,只有一个可用的轴“字重”,但是范围不同 - Oswald 的范围与升级为可变字体之前的范围相同,即 200 到 700,而 Hepta Slab 的极端发丝状字重为 1,一直到 900。

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

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

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

用例和优势

设置轴值取决于个人品味和应用排版最佳实践。任何新技术的危险在于可能被滥用,并且过于艺术化或探索性的设置也可能会降低实际文本的可读性。对于标题,探索不同的轴来创建出色的艺术设计令人兴奋,但是对于正文,这可能会使文本难以辨认。

令人兴奋的表达

Grass example by Mandy Michael

上面显示了一个艺术表达的绝佳示例,即 Mandy Michael 对字型 Decovar 的探索。

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

动画

Zycon 字型,由字体设计师和排版师 David Berlow(Font Bureau)设计,专用于动画。

还可以探索使用可变字体为字符制作动画的可能性。上面是使用 Zycon 字型的不同轴的示例。请参阅 Axis Praxis 上的实时动画示例

Anicons 是世界上第一个动画彩色图标字体,基于 Material Design Icons。Anicons 是一项实验,它结合了两种前沿字体技术:可变字体和彩色字体。

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

精细调整

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-weightwght 轴和通过 font-stretchwdth 轴。

传统上,您会将 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 的倾斜度轴从最小值更改为最大值。

使用光学尺寸

字型可以渲染得非常小(12px 的脚注)或非常大(80px 的标题)。字体可以通过更改其字母形状来响应这些尺寸变化,以更好地适应其尺寸。小尺寸可能最好不要精细细节,而大尺寸可能会从更多细节和更细的笔画中受益。

The letter 'a' shown at different optical sizes
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)。“等级”轴很有趣,因为它会在不更改字宽的情况下更改字体的字重,因此换行符不会更改。通过使用“等级”轴,您可以避免被迫调整影响整体字宽的“字重”轴的更改,然后调整影响整体字重的“字宽”轴的更改。

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 的 URL。可变字体概述列出了所有轴和值。

Google Variable Fonts Links 工具还可以为您提供完整可变字体的最新 URL。

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 常规字体)而没有其他字体,那么如果您切换到具有多个轴的可变字体,则字体大小可能不会净增加。与往常一样,这取决于您的用例。

另一方面,在设置之间为字体制作动画可能会导致性能问题。尽管一旦浏览器中对可变字体的支持更加成熟,这种情况将会得到改善,但是,通过仅对当前在屏幕上的字体制作动画,可以在某种程度上减少该问题。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 字重。

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

如果您正在使用 Google Fonts 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,依此类推。

谢谢

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

Hero image by Bruno Martins on Unsplash