react 组件中 canvas 使用自定义字体(如 'great vibes')时,常因字体未就绪导致回退到系统默认字体;本文提供一种轻量、可靠且无需第三方库的预加载方案——通过隐藏 dom 元素强制触发字体加载。
在 React 应用中使用
你当前代码中的 useEffect 在图像加载完成后立即调用 draw(),但此时 'Great Vibes' 极可能仍处于网络请求或解析阶段,导致首次渲染失败,仅刷新页面后才显示正常(此时字体已缓存)。
✅ 推荐解决方案:主动“预热”字体
利用浏览器渲染引擎的特性——只要某元素的 font-family 被计算并参与布局(哪怕内容为空、位置隐藏),浏览器就会触发该字体的加载。我们无需监听 document.fonts.load() 或引入复杂状态管理,只需在 Canvas 同级插入一个极小、不可见但明确声明目标字体的 DOM 元素:
{/* 关键:强制加载 Great Vibes 字体 */} AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
? 为什么这样有效?
⚠️ 注意事项与增强建议
@font-face {
font-family: 'Great Vibes';
src: url('../f
onts/GreatVibes-Regular.woff2') format('woff2'),
url('../fonts/GreatVibes-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap; /* 关键:避免阻塞渲染,同时允许重绘 */
}useEffect(() => {
const loadFont = async () => {
try {
await document.fonts.load("56px 'Great Vibes'");
// 字体就绪,可安全触发 draw
} catch (e) {
console.warn("Font load failed, using fallback");
}
};
loadFont();
}, []);但注意:document.fonts.load() 在部分旧浏览器中不支持,且需配合 Canvas 重绘逻辑,而“预热 DOM 元素”方案兼容性更好、零依赖、更简洁。
总结:Canvas 字体加载问题本质是渲染时机与资源加载异步性不匹配。通过一个隐藏的、声明了目标字体的 DOM 元素,我们巧妙地将字体加载“绑定”到 React 组件挂载流程中,无需修改 Canvas 逻辑、不增加额外状态、不引入竞态风险——这是兼顾可靠性、简洁性与兼容性的最佳实践。