页面字体闪一下?这两个标准能帮到你

本文做者来自 360 奇舞团的前端开发工程师何文力,同时也是 W3C CSS 工做组的成员javascript

现象

当咱们在浏览一些使用自定义字体的网站,或在开发中使用 @font-face 设置自定义字体时,时常会看到一个现象:页面结构和图片出来了,但文字区域是空白的。这种现象被称之为 FOIT (Flash Of Invisible Text)。css

MSNBC on Firefox

缘由

一般,咱们经过@font-face规则定义让浏览器加载使用第三方字体。这些写在 CSS 文件中的规则,浏览器必须待文件下载结束并解析以后才能开始下载字体文件。而要真正地触发字体文件下载,还要知足一些条件,根据 Zach Leatherman 的这篇文章,要触发字体下载,还要知足如下的条件:前端

  • 合法的 @font-face 规则,而且当前浏览器须要支持 src 列表中给出的格式
  • 文档中有节点使用了 @font-face 中相同的 font-family
  • 在 Webkit 和 Blink 引擎中,使用该 font-family 的节点不能为空
  • 若是 @font-face 中指定了 unicode-range,出现的文字内容还必须落在设定的 Unicode 范围中

当上述全部条件知足,浏览器才会开始下载字体文件,这也意味着,浏览器不单单须要解析 CSS 内容,还要解析页面内容才能决定是否须要下载字体。当浏览器开始下载字体,使用了该 font-family 的全部文本被隐藏,致使页面出现文本空白的状况。java

解决

在字体相关的 W3C 标准中,CSS Fonts Module Level 3 中的 font-display 属性以及 CSS Font Loading API 标准能够解决相关问题。web

font-display

font-display 属性添加于 CSS Fonts Module Level 3 中,已有大部分浏览器支持该属性。api

font-display 在 CSS 层面上提供了此类问题的解决方法,它提供了五个属性:浏览器

  • auto:使用浏览器默认的行为;
  • block:浏览器首先使用隐形文字替代页面上的文字,并等待字体加载完成再显示;
  • swap:若是设定的字体还未可用,浏览器将首先使用备用字体显示,当设定的字体加载完成后替换备用字体;
  • fallback:与 swap 属性值行为上大体相同,但浏览器会给设定的字体设定加载的时间限制,一旦加载所需的时长大于这个限制,设定的字体将不会替换备用字体进行显示。 Webkit 和 Firefox 中设定此时间为 3s;
  • optional:使用此属性值时,若是设定的字体没有在限制时间内加载完成,当前页面将会一直使用备用字体,而且设定字体继续在后台进行加载,以便下一次浏览时能够直接使用设定的字体。

CSS Font Loading API

除了 CSS 层面上解决问题,CSS Font Loading API 在 JavaScript 层面上也提供了解决方案。经过监听加载事件,咱们能够在字体加载完成后经过替换 class 达到 CSS 中 swap 属性值的效果。字体

浏览器支持方面也仍是通常优化

FontFace 接口支持状况网站

FontFaceSet接口支持状况

css-font-loading-api-comp-fontfaceset

标准中主要提供了FontFace接口加载字体,而且 document.fonts 对象为一个 FontFaceSet 接口,他是一组FontFace的集合,管理了页面上全部字体的状态。

FontFace 接受三个参数:font-family 名称、字体资源位置以及字体设定(可选)。

首先,要在 JavaScript 中加载字体,咱们要new一个 FontFace 并将其添加到全局 FontFaceSet 中:

const Aclonica = new FontFace('Aclonica', 'url(./Aclonica.ttf)');
// 添加到全局的 FontFaceSet 中
document.fonts.add(Aclonica);
复制代码

第二步:调用 FontFaceload 方法开始加载,load方法将返回一个 Promise。当咱们的字体加载完以后,就能够经过变换 class 的换上新字体。

Aclonica.load().then(() => {
    // 当字体加载完以后,咱们就能够经过替换 class 的方法替换掉默认的字体
    // 此处的逻辑也能够是你的字体渲染策略
    document.body.classList.add('use-aclonica');
})
复制代码
.use-aclonica {
    font-family: Aclonica;
}
复制代码

不够完美

咱们经过上述方法进行优化以后,虽然没有了 FOIT 现象,可是实际效果倒是这样的:

fout

咱们发现,字体加载完以后页面仍是不可避免地闪了,这是因为备用字体和定义的字体外形相差过大致使的视觉效果,这种现象又被称之为 FOUT (Flash Of Unstyled Text)。对于正在阅读文章的用户来讲,显然不是很好的体验。

小结

CSS Fonts Module 以及 CSS Font Loading API 这两组标准都给前端字体不管在渲染上仍是控制上赋予了更多能力,随着咱们更深刻地研究规范,相信会给字体加载以及渲染方面的问题带来更多的解决方案。

文内连接

www.zachleat.com/web/compreh…

drafts.csswg.org/css-font-lo…

致谢

感谢李松峰老师对本文提出的修改建议

相关文章
相关标签/搜索