You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:15:31