基于 @font-face 加载
2024年08月06日
一、认识
二、动态加载字体文件
需求描述
: 字体包体积巨大, 基本上都在几M
左右。 所以,在以下场景需要考虑按需加载字体:
- 场景一: 实时编辑一个
DOM
元素, 可以切换字体。如果直接将全部字体写入到样式文件中,IE
浏览器首先会下载全部注册声明的字体,即使未使用该字体。谷歌等浏览器实现了按需加载字体文件的功能。但是我们在使用html2-canvas
或者dom-to-image
第三方DOM
转Image
的时候, 会下载完全部字体,然后再开始解析、转化, 增加了DOM
转Image
的时间。
解决方案: 当需要加载字体时,向 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
,动态加载字体。
实现如下:
- Web
- Node
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
样式。浏览器会自行解析并在下载完成字体后显示中文字体。
var Fontmin = require('fontmin');
const path = require('path');
module.exports = function(req, res, next) {
// 字体源文件
var srcPath = path.join(__dirname, '../../static/huiwenmingchao.otf');
var text = req.query.text;
// 文字去重
var textArr = Array.from(new Set(text.split('')));
text = textArr.join('');
// 初始化
var fontmin = new Fontmin().src(srcPath).use(
// 字型提取插件
Fontmin.glyph({
text: text // 所需文字
})
);
fontmin.run(function(err, files, stream) {
if (err) {
// 异常捕捉
console.error(err);
}
res.send(files[0].contents);
});
};
基于express
框架实现一个API
,动态字体从参数传入,路由中使用fontmin
模块对字体进行抽取,抽取后的buffer
直接返回给前端。