跳到主要内容

基于 @font-face 加载

2024年08月06日
柏拉文
越努力,越幸运

一、认识


二、动态加载字体文件


需求描述: 字体包体积巨大, 基本上都在几M左右。 所以,在以下场景需要考虑按需加载字体:

  • 场景一: 实时编辑一个 DOM 元素, 可以切换字体。如果直接将全部字体写入到样式文件中, IE 浏览器首先会下载全部注册声明的字体,即使未使用该字体。谷歌等浏览器实现了按需加载字体文件的功能。但是我们在使用 html2-canvas 或者 dom-to-image 第三方 DOMImage 的时候, 会下载完全部字体,然后再开始解析、转化, 增加了DOMImage的时间。

解决方案: 当需要加载字体时,向 DOM 添加 @font-face,动态加载字体。

实现如下:

const lazyLoadFontFamily = (fontFamily: string) => {
if (!fontFamily) {
return;
}
const documentStyle = document.createElement('style');
const fontFace = `
@font-face{
font-family: ${fontFamily};
src: url(${currentCdnUrl}custom-course-cover/fonts/${fontFamily}.ttf);
}
`;
documentStyle.appendChild(document.createTextNode(fontFace));
document.head.appendChild(documentStyle);
};

但是这里存在一个问题,由于请求接口需要一定的时间,文字会出现先空白一段时间后显示的现象。这种现象被称之为 FOIT (Flash Of Invisible Text)。我们需要寻找优化办法。 font-display 属性在 CSS 层面上提供了此类问题的解决方法。使用 font-display: swap 我们可以采用先使用系统默认字体,在中文字体下载完成后替换。

三、动态加载具体字体


解决方案: 当需要加载字体时,向 DOM 添加 @font-face,动态加载字体。

实现如下:

const lazyLoadFontFamily = (fontFamily: string) => {
if (!fontFamily) {
return;
}
const documentStyle = document.createElement('style');
const fontFace = `
@font-face{
font-family: ${fontname};
src: url("http://xxxx:8080/api/webfont?font=${fontname}&text=${encodeURIComponent(text)}`;

documentStyle.appendChild(document.createTextNode(fontFace));
document.head.appendChild(documentStyle);
};

通过 JavaScript 添加一个 CSS 样式。浏览器会自行解析并在下载完成字体后显示中文字体。

参考方案


动态中文字体加载方案