Material Icons字体初始渲染为文本,如何实现加载完成后再显示?
解决Material Icons初始渲染显示文本的问题
问题背景
在使用Material Icons字体时,组件初始渲染会先显示图标对应的文本(浏览器 fallback 字体),待字体加载完成后才切换为图标。需要在字体加载完成前隐藏图标区域,且不想修改Webpack配置处理@font-face。
可行解决方案
1. 改进Font Loading API逻辑(解决缓存判断问题)
之前的document.fonts.check在页面刷新时可能无法立即检测到缓存的字体,导致短暂显示空白。通过主动触发字体加载+监听加载事件,同时先同步检查缓存状态,可以解决这个问题:
import { useState, useEffect } from 'react'; // 组件内部逻辑 const [isFontLoaded, setIsFontLoaded] = useState(false); useEffect(() => { // 先同步检查字体是否已缓存加载 const checkFont = () => { const loaded = document.fonts.check('1em "Material Icons"'); setIsFontLoaded(loaded); }; checkFont(); // 主动触发字体加载(针对未缓存情况) document.fonts.load('1em "Material Icons"').then(checkFont); // 监听所有字体加载完成事件作为兜底 const handleLoadingDone = () => checkFont(); document.fonts.addEventListener('loadingdone', handleLoadingDone); return () => { document.fonts.removeEventListener('loadingdone', handleLoadingDone); }; }, []); // 渲染部分 return ( isFontLoaded ? ( <div className="text-left d-flex align-items-center" style={{ fontFamily: "Material Icons", fontSize: "24px", opacity: parentActive || parentExpanded ? 1 : 0.8, padding: "0 9px", maxWidth: "45px", overflow: "hidden", height: "2.5rem", color: parentActive || parentExpanded ? color.primary : undefined, }} > <span aria-hidden>{iconName}</span> </div> ) : ( <div style={{ width: "42px", height: "40px" }} /> {/* 保持占位,无可见内容 */} ) );
优势:无需修改现有图标使用逻辑,完美处理缓存字体的立即显示,布局稳定。
2. CSS隐藏文本(避免DOM替换闪烁)
保持图标容器始终存在,仅通过CSS隐藏未加载字体时的文本,字体加载后再恢复显示:
import { useState, useEffect } from 'react'; // 组件内部逻辑 const [isFontLoaded, setIsFontLoaded] = useState(false); useEffect(() => { const checkFont = () => { setIsFontLoaded(document.fonts.check('1em "Material Icons"')); }; checkFont(); document.fonts.load('1em "Material Icons"').then(checkFont); document.fonts.onloadingdone = checkFont; }, []); // 渲染部分 return ( <div className="text-left d-flex align-items-center" style={{ fontFamily: "Material Icons", fontSize: "24px", opacity: parentActive || parentExpanded ? 1 : 0.8, padding: "0 9px", maxWidth: "45px", overflow: "hidden", height: "2.5rem", color: parentActive || parentExpanded ? color.primary : undefined, position: 'relative' // 为绝对定位的文本提供参考 }} > <span aria-hidden style={{ position: isFontLoaded ? 'static' : 'absolute', left: isFontLoaded ? 'auto' : '-9999px', visibility: isFontLoaded ? 'visible' : 'hidden' }} > {iconName} </span> </div> );
优势:DOM结构不变,避免切换DOM导致的布局闪烁,用户感知更流畅。
3. 替换为SVG图标(彻底消除字体依赖)
使用Material Icons的SVG版本(比如通过@mui/icons-material库),直接渲染矢量图形,完全避免字体加载问题:
// 导入对应SVG图标组件,需根据iconName做映射 import { Menu as MenuIcon, Home as HomeIcon } from '@mui/icons-material'; // 建立iconName到SVG组件的映射 const iconMap = { 'menu': MenuIcon, 'home': HomeIcon, // 其他图标映射... }; const IconComponent = iconMap[iconName]; // 渲染部分 return ( <div className="text-left d-flex align-items-center" style={{ fontSize: "24px", opacity: parentActive || parentExpanded ? 1 : 0.8, padding: "0 9px", maxWidth: "45px", overflow: "hidden", height: "2.5rem", color: parentActive || parentExpanded ? color.primary : undefined, }} > {IconComponent && <IconComponent aria-hidden />} </div> );
优势:彻底摆脱字体文件依赖,无需等待字体加载,图标渲染更快,样式控制更灵活。
内容的提问来源于stack exchange,提问作者jaredbftball
相关产品推荐
相关产品推荐

