鸿蒙Cordova开发踩坑记录:字体加载的“闪烁“ (FOUT)
摘要:游戏UI使用Web Font时出现FOUT(字体闪烁)问题,表现为启动时先显示默认字体,随后才切换为自定义字体。本文提出三种解决方案:1)Preload预加载字体文件;2)将子集化字体转为Base64内嵌到CSS中实现0延迟加载;3)Native层共享字体。其中Base64方案效果最佳,通过工具将字体从2MB压缩到12KB后内联,彻底消除闪烁。核心思路是消除字体加载等待时间,提升用户体验。
摘要:为了让游戏 UI 更具个性,我们使用了自定义的 Web Font(如 “Clear Sans”)。但在应用启动时,文字会先显示为系统默认的宋体,几百毫秒后才突然"跳变"成自定义字体。这种现象被称为 FOUT (Flash of Unstyled Text)。本文记录了如何通过预加载和 Base64 内联方案,彻底根治字体闪烁。
😵 1. 视觉违和感
现象:
- 应用冷启动。
- Logo 下方的 “2048” 文字显示为细细的默认字体(Sans-serif)。
- 0.5秒后,文字突然变粗、变圆润(加载好了 Clear Sans)。
- 布局发生微小的位移(抖动)。
这种体验非常廉价,仿佛应用还没准备好就匆忙见客了。
📉 2. 瓶颈分析
浏览器加载字体的默认行为是:
- 解析 HTML,发现 CSS。
- 解析 CSS,发现
@font-face。 - 渲染 DOM 树。
- 遇到使用了该字体的文本节点时,才开始下载字体文件 (.woff2)。
- 下载期间,使用备用字体显示(或透明,取决于
font-display)。 - 下载完成,替换字体。
关键瓶颈在于第 4 步:延迟加载。
🛠️ 3. 优化方案
3.1 方案一:Preload 预加载
告诉浏览器:“这个字体很重要,别等发现了再下,现在就下!”
在 index.html 的 <head> 最顶部加入:
<link rel="preload" href="fonts/ClearSans-Bold.woff2" as="font" type="font/woff2" crossorigin>
注意:crossorigin 属性是必须的,即使字体在本地。因为 Web 字体默认是跨域请求。
3.2 方案二:Base64 内联 (终极杀招) ✅
由于我们的字体文件是从本地 file:// 或 resource:// 加载的,IO 操作虽然快,但依然有异步延迟。
对于核心字体(如标题字体),我们可以直接将其转为 Base64 编码,嵌入到 CSS 文件中。
优点:CSS 加载完,字体就有了。0 延迟。
缺点:CSS 文件体积变大。但对于只有几 KB 的子集化字体(Subsetting),这完全可以接受。
@font-face {
font-family: 'Clear Sans';
font-weight: 700;
/* 直接嵌入 Base64 */
src: url('data:font/woff2;base64,d09GMgABAAAAAApQAA0AAAAAFWAAA...') format('woff2');
/* 关键:swap 策略作为保底,但在 Base64 方案下其实用不上 */
font-display: block;
}
3.3 方案三:Native 字体注入
如果多个 Web 页面共用同一套字体,为了避免重复加载,我们可以利用 ArkWeb 的 RegisterCustomFont (如有支持) 或者在 Native 层加载字体。
但在目前的混合架构中,更简单的做法是将字体文件放在 rawfile 中,通过 resource:// 协议共享。
🎨 4. 字体子集化 (Subsetting)
为了减小字体体积(从而减小 Base64 字符串长度),我们使用了 font-spider 或 pyftsubset 工具,只提取游戏中用到的字符(数字 0-9,字母 G, A, M, E, O, V, E, R…)。
效果:
原始 .ttf 大小:2 MB
提取后 .woff2 大小:12 KB
这 12KB 的 Base64 嵌入到 CSS 中,几乎不影响解析速度,但换来了完美的 0 秒闪烁 体验。
📚 5. 总结
解决字体闪烁的核心思路是 “消除等待”。
- 对于全量字体:使用
<link rel="preload">。 - 对于关键标题/数字:使用 Subsetting + Base64 Inline。
- 配置
font-display: block避免文字隐形,或者font-display: swap接受闪烁但保证可见。
在 2048 项目中,我们选择了方案二,效果完美。
更多推荐




所有评论(0)