本文做者来自 360 奇舞团的前端开发工程师何文力,同时也是 W3C CSS 工做组的成员javascript
当咱们在浏览一些使用自定义字体的网站,或在开发中使用 @font-face
设置自定义字体时,时常会看到一个现象:页面结构和图片出来了,但文字区域是空白的。这种现象被称之为 FOIT (Flash Of Invisible Text)。css
一般,咱们经过@font-face
规则定义让浏览器加载使用第三方字体。这些写在 CSS 文件中的规则,浏览器必须待文件下载结束并解析以后才能开始下载字体文件。而要真正地触发字体文件下载,还要知足一些条件,根据 Zach Leatherman ① 的这篇文章,要触发字体下载,还要知足如下的条件:前端
@font-face
规则,而且当前浏览器须要支持 src
列表中给出的格式@font-face
中相同的 font-family
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
属性添加于 CSS Fonts Module Level 3 中,已有大部分浏览器支持该属性。api
font-display
在 CSS 层面上提供了此类问题的解决方法,它提供了五个属性:浏览器
swap
属性值行为上大体相同,但浏览器会给设定的字体设定加载的时间限制,一旦加载所需的时长大于这个限制,设定的字体将不会替换备用字体进行显示。 Webkit 和 Firefox 中设定此时间为 3s;除了 CSS 层面上解决问题,CSS Font Loading API ② 在 JavaScript 层面上也提供了解决方案。经过监听加载事件,咱们能够在字体加载完成后经过替换 class 达到 CSS 中 swap
属性值的效果。字体
浏览器支持方面也仍是通常优化
FontFace
接口支持状况网站
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);
复制代码
第二步:调用 FontFace
的 load
方法开始加载,load
方法将返回一个 Promise
。当咱们的字体加载完以后,就能够经过变换 class 的换上新字体。
Aclonica.load().then(() => {
// 当字体加载完以后,咱们就能够经过替换 class 的方法替换掉默认的字体
// 此处的逻辑也能够是你的字体渲染策略
document.body.classList.add('use-aclonica');
})
复制代码
.use-aclonica {
font-family: Aclonica;
}
复制代码
咱们经过上述方法进行优化以后,虽然没有了 FOIT 现象,可是实际效果倒是这样的:
咱们发现,字体加载完以后页面仍是不可避免地闪了,这是因为备用字体和定义的字体外形相差过大致使的视觉效果,这种现象又被称之为 FOUT (Flash Of Unstyled Text)。对于正在阅读文章的用户来讲,显然不是很好的体验。
CSS Fonts Module 以及 CSS Font Loading API 这两组标准都给前端字体不管在渲染上仍是控制上赋予了更多能力,随着咱们更深刻地研究规范,相信会给字体加载以及渲染方面的问题带来更多的解决方案。
① www.zachleat.com/web/compreh…
② drafts.csswg.org/css-font-lo…
感谢李松峰老师对本文提出的修改建议