@font-face 概述

@font-face是一个CSS3新增的一个@规则,通过自备字体包的方式,允许开发者指定网页的字体,消除网页对用户电脑字体的依赖。

使用方式如下:

@font-face {
  font-family: 'myFont';
  src: url('path/to/font');
  font-weight: 400;
  font-style: normal;
}

一般会指定多个url,引入该字体的多种格式文件(eot、woff、tff、svg、otf),增加浏览器兼容性。但是以目前最新的浏览器支持度来看,几乎所有浏览器都支持TTF/OTF这两种字体格式了。所以如果不需要兼容一些旧浏览器,可以只引入TTF或OTF的字体格式文件即可。

font-weightfont-style不写也可以,默认值就是400normal。但是通过指定这两个值,可以对同一字体使用不同的字体文件。

例如:

@font-face {
  font-family: 'myFont';
  src: url('path/to/font-normal');
  font-weight: 400;
}
@font-face {
  font-family: 'myFont';
  src: url('path/to/font-bold');
  font-weight: 700;
}

这样定义后,在css中指定使用myFont字体,并指定font-weight:400时,就会使用第一个@font-face规则; 如果指定font-weight:700时就会使用第二个@font-face规则

@font-face 带来的困扰

因为@font-face引入了字体包,页面首次加载的时候浏览器需要下载这些字体包,而在字体文件加载完成之前,浏览器会使用缺省字体显示内容。这种情况叫 FOUT(Flash Of Unstyled Text)。但是这里会有一个问题,除了IE,其他浏览器都会等待3秒后才显示默认字体,这就会造成3秒的字体缺失。这种情况叫FIOT(Flash Of Invisible Text)。

FOUT & FOIT (图片来自网络)

解决困扰

上述问题我们可以通过检测字体加载完成后给页面运用自定义字体。目前CSS3的Font Loading API 可以用来解决这个困扰,但是还是处于草案阶段,不适合生产环境。(后续补充该用法)

这时就轮到字体加载库出场了,github上有不少字体加载的库,比较好的有Font Face ObserverWeb Font Loader。我个人比较喜欢Font Face Observer的用法

这样就可以借助这个库,在字体加载完成后,给根元素加个Class,使用我们自定义的字体,这样就可以避免FOIT的情况。

首先定义@font-face和使用字体的类

@font-face {
  font-family: 'SourceHanSans';
  src: url('../font/SourceHanSans/SourceHanSansCN-Normal.otf');
  font-weight: 400;
}

@font-face {
  font-family: 'SourceHanSans';
  src: url('../font/SourceHanSans/SourceHanSansCN-Bold.otf');
  font-weight: 700;
}

.f-shs body {
  font-family: "SourceHanSans", "PingFang SC", "Helvetica Neue", Helvetica, Arial;
}

body {
  font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial;
}

页面默认是使用body元素选择定义的字体,没使用SourceHanSans字体,所以页面不会出现FOIT的情况。然后使用Font Face Observer在字体加载完成后,给html根元素加上f-shs的类名

import FontFaceObserver from 'fontfaceobserver'

let shsNormal = new FontFaceObserver('SourceHanSans')
let shsBold = new FontFaceObserver('SourceHanSans', {
  weight: 700,
})

Promise.all([
  shsNormal.load(null, 60000),
  shsBold.load(null, 60000)
]).then(() => {
  document.documentElement.className += 'f-shs'
})

这样就实现了FOUT的效果,而不会出现3秒的字体缺失问题。

参考链接

https://xiaoiver.github.io/coding/2018/03/22/%E5%AD%97%E4%BD%93%E5%8A%A0%E8%BD%BD%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5.html