CSS3 使用@font-face引入字体的兼容性方案及优化
引入第三方字体一定要注意字体版权问题
# 前言
承接上文web 端字体兼容性适配之后,好久没有总结 CSS3 引入字体 @font-face
相关的文章了。一是在掌握 @font-face
的基本使用要领后没遇过兼容性问题,二是觉得 @font-face
已经兼容 ie9+及现代浏览器了,兼容性问题基本可以忽略不计了。事与愿违的是,在多次实战中发现引入第三方字体会出现许多奇形怪状的问题,譬如字体文件的编码问题引发字体可以在桌面系统正常使用而在 Web 端不起作用、字体文件中英文或其他外语的字体行高差异问题、字体文件的等宽比在不同浏览器显示差异问题......现对web 端字体兼容性适配作补充,如果不妥请各位大佬抬手指正(#^.^#)。
# css 引入外部字体
css 通过@font-face
的方式引入外部字体。
且不同浏览器对引入字体的格式有差别
浏览器的兼容性,如下所示:
Browser | @font-face | TrueType | WOFF | EOT | SVG | SVGZ |
---|---|---|---|---|---|---|
IE | 4+ | 9+ | 9+ | 4+ | ||
Firefox | 3.5+ | 3.5+ | 3.6+ | |||
Chrome | 4+ | 4+ | 6+ | 4+ | 6+ | |
Safria | 3.1+ | 3.1+ | 6+ | 3.1+ | 3.1+ | |
Opera | 10+ | 10+ | 11.1+ | 10+ | 10+ |
由上归纳可得:
.TTF
或.OTF
,适用于IE 9+、Firefox 3.5+、Safari 3.1+、Opera 10+
.EOT
,适用于Internet Explorer 4.0+
.SVG
,适用于Chrome、IPhone
最终得出结论:.eot + .ttf /.otf + svg + woff
= 所有浏览器的完美支持
# @font-face
的语法规则
@font-face
的语法规则如下:
@font-face {
[font-family: <family-name>;]?
[src: [ <uri> [format(<string>#)]? | <font-face-name> ]#;]?
[unicode-range: <urange>#;]?
[font-variant: <font-variant>;]?
[font-feature-settings: normal|<feature-tag-value>#;]?
[font-stretch: <font-stretch>;]?
[font-weight: <weight>];
[font-style: <style>];
}
2
3
4
5
6
7
8
9
10
对此,我们需要事先准备需要引入字体源件,然后通过 css 定义@font-face
进行引入。
@font-face {
font-family: 'YourWebFontName';
src: url('YourWebFontName.eot'); /* IE9 Compat Modes */
src: url('YourWebFontName.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('YourWebFontName.woff') format('woff'),
/* Modern Browsers */ url('YourWebFontName.ttf') format('truetype'), /* Safari, Android, iOS */
url('YourWebFontName.svg#YourWebFontName') format('svg'); /* Legacy iOS */
}
.ff-fcustom {
font-family: 'YourWebFontName';
}
2
3
4
5
6
7
8
9
10
11
# 字体兼容性
# 字体编码兼容
前段时间有几个需要引入第三方字体的项目,引入过西文、韩文、泰文、以及种类繁多让你眼花缭乱的中文字体,可能是因为引入那种中文字体比较多的缘故吧,老觉得中文字体的兼容性可能会多些,其中编码兼容性像一座大山。
或许你会遇到过这样的情景,字体在 Photoshop 可以正常使用,放在 Web 端通过 @font-face
引入后字体显示不起作用。与相似的还有引入字体在 IE、Firefox 等浏览器正常使用,在 chrome 浏览器却显示编码报错不起作用如:OTS parsing error: cmap: Failed to serialize table
的情况,或者完全因为字体编码格式不是被该浏览器所支持。
“工欲善其事必先利其器”,解决编码问题最直接的办法当然是将字体转码处理了。这里推荐两个字体转码工具:
只需将字体转化为需要的字体格式即可,另外推荐使用.ttf
格式,用途下面有细讲。
# 字体优化
# 字体压缩
字体文件体积一般少则 2~4M,多则能达到 20~30M 之多,因此 直接 引入第三方字体尚且存在很大缺陷,尤其是在移动端使用。
当然开发者的智慧是无穷的,社区对此类问题也提供了字体压缩的解决方案。
常用字体压缩有:
font-spider
fontmin
上述字体压缩工具的核心是,通过抽离指定预设的字符串来生成新的字体文件,因为排除了大量不需要的字符串集,抽离后的字体文件体积大大变小,从而达到字体压缩的效果。
# font-spider
使用font-spider
已经有几年时间了,用着方便也是比较推荐使用的。
详细的使用方法可以查看font-spider 官网。
font-spider
的主要用途是 将 .ttf
格式的字体文件根据需求预设 css 字体格式来生成字体文件。
特别注意的是,font-spider
只能将 .ttf
格式的字体文件转化为其他格式的字体文件。
# fontmin
最近身边有同学也在使用 fontmin
来转化字体格式,因此也贴出来供大家参考食用。
详细的使用方法可以查看fontmin 官网。
fontmin
跟 font-spider
的最大差异是,前者不需要指定**.ttf
** 格式的字体文件进行转化,而是通过其配套的字体转化插件来定制使用。
# 使用 rel="preload"
优化字体加载
由于自定义字体只有当前页面被引用到的时候,浏览器才会对字体进行加载。为了更快的加载字体文件,可以使用rel="preload"
来达到效果
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />
# FOUT(Flash of Unstyled Text) 优化
FOIT(Flash of Invisible Text)问题:浏览器在加载字体的时候的默认表现形式,也就是在字体加载过程中,页面是看不到文本内容的。在现代浏览器中,FOIT 会导致这种现象出现至多 3 秒。FOIT 会导致很差的用户体验,这是我们需要尽量去避免的。
FOUT:在字体加载过程中使用默认的系统字体,字体加载完后显示加载的字体,如果超过了 FOIT(3s)字体还没加载,则继续使用默认的系统字体。
FOUT 特性:
- 通过
@font-face
设置 属性font-display
来实现,默认为 auto。 - IE 浏览器和 Edge 不会等待 FOIT 超时才显示默认字体,会立即显示默认字体。FOUT 比 FOIT 好,但是需要注意它引起的 reflow。
为了实现 FOUT,一般可设置 font-display
为 swap
、fallback
或 optional
。
swap
:为字体提供一个非常小的阻塞周期和无限的交换周期。(自定义字体加载成功即显示字体样式,可以在页面中看到的一个字体替换的效果)fallback
:为字体提供一个非常小的阻塞周期和短暂的交换周期。(Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a short swap period (3s is recommended in most cases).)optional
:为字体提供一个非常小的阻塞周期,并且没有交换周期。(Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a 0s swap period)
@font-face {
font-family: 'fcustom';
font-display: 'swap';
font-display: 'fallback';
font-display: 'optional';
}
2
3
4
5
6
# 使用监听字体加载
# FontFaceObserver
通过使用插件 npm i fontfaceobserver
来监听字体加载完成,从而按需设置字体样式。
在字体加载前预算一种字体或使用 loading 效果什么的,字体加载成功后切换字体即可
// css 中 @font-face 已定义好
import FontFaceObserver from 'fontfaceobserver';
function loadfont() {
var ffo = new FontFaceObserver('My Family');
ffo.load().then(() => {
document.getElementById('fcustom').style.fontFamily = 'My Family';
});
}
2
3
4
5
6
7
8
9
# Font Load API
方法效果同 FontFaceObserver。
使用 Font Load API 加载字体,字体在加载完成前预算一种字体,字体加载完成切换相应的 CSS 即可。
但是需注意 Font Load API 是原生的 API,有兼容性问题。
<script>
document.fonts.load('1em open_sansregular').then(function() {
var docEl = document.documentElement;
docEl.className += ' open-sans-loaded';
});
</script>
<style>
.open-sans-loaded h1 {
font-family: open_sansregular;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
# 字体转 BASE64URI
核心要义就是将@font-face 中定义字体时的路径直接改为字体的 base64 编码。
- 优点:因为不会产生 FOIT 和 FOUT。所以不会有 reflow 和 repaint。
- 缺点:
- 字体转成 base64 也会很大,会影响页面首次加载速度。
- 不支持逗号分隔的形式加载多种格式的字体,只能加载一种格式字体。这导致你为了尽可能保证所有浏览器都可以兼容,通常会指定为 woff 格式,因为 woff 格式兼容性好,但是却没法使用更小体积的 woff2 格式,因为 woff2 格式兼容性差点。
# 异步加载 BASE64 格式 URI 字体
通过异步的方式插入带有 BASE64 格式 URI 字体的 CSS 链接。
至此笔者已经词穷了,下面是 CV 大佬们的博文来的,文末已贴出出处。
# FOFT(Flash of Faux Text)
FOFT 会把字体的加载分成多个部分,首先加载罗马网络字体,然后会在加载真实的粗体和斜体的时候立即使用 font-synthesis 属性渲染粗体和斜体的变体。
这种方法是基于[使用 Font Load API + FOUT + class 切换]这种方式的,非常适合加载同一种字体但是不同粗细,字形的场景,比如罗马、粗体、斜体、粗斜体等。
我们将这些字体分成 2 阶段:
- 第一阶段是罗马字体
- 然后立即渲染人造粗体和斜体
- 最后(第二阶段)用真实字体替代
这里面还可以使用 sessionStorage 优化访问重复视图的场景。
# CRITICAL FOFT
CRITICAL FOFT 和标准的 FOFI 的唯一区别就在于第一阶段罗马字体的加载,CRITICAL FOFT 不会加载罗马字体的全集,只会加载它的一个子集(比如 A-Za-z0-9),全集会在第二阶段加载。
# CRITICAL FOFT WITH DATA URI
和 CRITICAL FOFT 的唯一区别就是罗马子集字体的加载方式,前面是用 Font Load API 完成了,这里会将马子集字体硬编码成 BASE64 URI 的形式加载。
# CRITICAL FOFT WITH PRELOAD
同上面的唯一区别还是第一阶段罗马子集字体的加载方式,它采用的是 preload 的形式加载。