如何让Web字体加载的用户体验更好

3,977 阅读6分钟

当用户第一次访问我们的网站时,如果网站使用的时Web字体,通常时需要下载所有的Web字体的。

在大多数情况下,当HTML文件已经在浏览器中加载完成后,Web字体文件可能仍然还在下载中。

这种情况下,浏览器还无法使用Web字体。那我们应该怎么办呢?

通常有两种选择:

  1. 等到Web字体下载完成后再显示文本。
  2. 使用后备字体渲染文本,即用户设备本地上安装的字体。

但是这两种方法都会带来新的问题。第一种方法会出现隐形文本闪烁(FOIT — Flash Of Invisible Text)的问题。 而第二种方法则会出现无样式文本闪烁(FOUT — Flash of Unstyled Text)。

以上问题可能在你看来并没有怎么遇到过。这有可能是:

  • 字体已经安装在你的设备上了
  • 你的网络环境速度已经很快了
  • 通常只有在第一次访问页面时才会存在这些问题,因为浏览器已经缓存字体文件了,所以再次访问就不会出现此问题了

只要用户首次访问我们的页面时需要下载Web字体,这个问题就会存在。然而,幸运的是,我们可以用一些现代的 CSS 来改进一些东西。

font-display属性

要控制字体在可用之前应该如何渲染,我们可以利用 font-display 属性。

它包含在我们的@font-face 声明中:

@font-face {
  font-family: 'Great Vibes';
  src: url('/fonts/great-vibes.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap; /* 👈👈👈 */
}

当我们添加 Google Font 代码片段时,它包含一个查询参数来设置这个属性:

google-fonts-display-swap.png

为了理解这个属性,我们需要讨论 font-display 时间线。

当浏览器渲染使用Web字体的HTML元素时,会启动一个计时器。这个计时器有三个阶段:

  • 阻塞期(The block period),文本不可见,一旦Web字体下载完成,浏览器就会渲染文本。
  • 交换期(The swap period),浏览器使用后备字体(也就是font-family中第一个可用的字体)渲染文本,一旦Web字体下载完成,就使用Web字体替换后备字体文本
  • 失效期(The failure period),如果字体在阻塞或交换期间没有加载完成,浏览器就会停止尝试,并且不管 Web 字体之后即使下载并加载完成了,仍然显示后备字体

每个周期有多长?这取决于 font-display 属性。实际上,font-display 是一种控制每个窗口长度的方法。

让我们来看看这些选项:

Block

block.png

font-display: block; 会把字体的可用性放在首位,它有一个相对较长的时间段和无限长的交换期。

该属性在CSS规范没有提供这些时间段的明确时间持续时间,但提供了推荐的最大值。对于 font-display: block,块周期应该不超过3秒。

block应该在字体绝对重要的时候使用。例如使用图标字体,如果图标字体没有加载完成就显示文本,用户可能会看到随机字母而不是图标。

Swap

swap.png

使用font-display: swap几乎没有阻塞周期,并且拥有无限长的交换周期。其目地时尽可能快的获得渲染文本。

这也是Google字体采用的方式。这是一个很好的选择,但是在大多数情况下其实还有更好的选择, 那就是fallback。

Fallback

fallback.png

font-display: fallback 是最复杂的,它具有一个非常短的阻塞周期(约100ms)和一个恰当的交换周期(约3s)。

它的用户体验最好:

  • 在高速网络中,字体可以在阻塞周期内完成下载,这可以避免字体闪烁的出现。
  • 在网络不好的情况下,备用字体将会被使用,防止页面加载完成一段时间后出现字体闪烁。

Optional

optional.png

最后,加载的字体并不是特别重要而只是有细微的改进时,font-display: optional也是一个不错的选择。它的特点是有一个短的阻塞周期(100ms或更短),并且没有交换周期。如果字体不能立即加载,它将不会被使用。

通常,使用optional值意味着用户在首次访问时,看到的将会是备用字体,不会有页面闪烁。但是后续的页面都会使用Web字体。因为首次访问页面后,Web字体已经加载完成了。

以上就是font-display属性不同值之间的区别。

字体匹配

如果使用包含有交换周期的font-display属性值(除optional外), 渲染的文本会出现一个转换的过程,如果两种转换的字体之间差异很大,可能会出现令人不愉快的布局偏移,如下图所示:

fontswap.gif

一种可能的解决方案是使用诸如 font-size/letter-spacing/line-height 之类的属性,使两种字体尽可能接近。

下面时调整后的效果:

fontswap-tweaked.gif

Font style matcher”是由 Monica Dinculescu创建的一个漂亮的工具,它可以帮助你找到匹配这两种字体所需的 CSS:

font-style-matcher.gif

棘手的事情是,我们需要这些声明只适用于我们的字体加载之前。在纯 CSS 中没有这样的方法,但是可以在 JS 中使用 Font Loading API 来实现。

浏览器厂商们正在积极开发一种新的特性,称为“字体描述符(font descriptors)”(也称为 f-mods)。这让我们可以调整后备字体的特征,以匹配 Web 字体,从而实现更自然的字体转换。下面是这个特性的使用方式:

@font-face {
  font-family: "Great Vibes";
  src: url('/fonts/great-vibes.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fallback";
  size-adjust: 95%;
  ascent-override: 90%;
  descent-override: 120%;
  src: local("Arial");
}
body {
  font-family: "Great Vibes", "Fallback", sans-serif;
}

第二个font-face声明中的Fallback并不是指真正的字体而是代指src中的字体。local表示冲本地获取Web字体。

size-adjust用来调整字体大小。ascent-overridedescent-override用来更改行高,也就是更改line-height属性所调整的基线高度。

但是目前这些属性还没有得到广泛的支持。size-adjust只能在 Chrome 和 Firefox 的开发版本中使用,在其他浏览器中不能使用。不过,在未来,这将是一种低成本改进字体加载体验的方法。

参考链接:How to avoid layout shifts caused by web fonts