React Three Fiber使用TextGeometry报迁移错误且刷新后失效问题
问题产生原因
报错和刷新后文字消失的问题由三个核心问题共同导致:
- Three.js 官方将
TextGeometry移动到examples目录后,类内部自带路径校验逻辑,会主动检查全局THREE对象下是否存在TextGeometry属性,不存在就抛出指定路径迁移的报错。仅通过@react-three/fiber的extend方法注册自定义元素,不会修改全局THREE对象,因此会触发校验报错。 - 字体解析逻辑写在组件模块顶层,React 18+默认开启的严格模式会重复执行模块副作用,导致font实例重复生成、TextGeometry重复注册,刷新页面时出现引用冲突,geometry生成失败。
- 代码中未给承载文字的
<mesh>添加材质,同时环境光强度设置过低(仅0.1),首次加载时因资源缓存可能碰巧显示,刷新后重新走渲染流程时,无材质的网格不会被WebGL绘制,加上光照不足,文字视觉上完全消失。
修复步骤
- 引入TextGeometry后,手动将其挂载到全局THREE对象上,绕过内部路径校验
- 将字体加载、参数配置逻辑移入组件内部,用R3F提供的
useLoader加载字体资源,用useMemo缓存geometry配置,避免重复实例化引发的引用冲突 - 为文字网格添加标准材质,调整光照参数到合理范围,保证文字可见
- 给
<textGeometry>添加绑定文本内容的key,文本变化时正确重建几何体
修复后完整代码
TextMesh 组件
import { extend, Object3DNode, useLoader } from '@react-three/fiber'; import * as React from 'react'; import * as THREE from 'three'; import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'; // 手动挂载到THREE全局,解决内部路径校验报错 THREE.TextGeometry = TextGeometry; import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'; import monster from '../assets/fonts/Montserrat_Bold.json'; // 仅执行一次元素注册 extend({ TextGeometry }); declare global { namespace JSX { interface IntrinsicElements { textGeometry: Object3DNode<TextGeometry, typeof TextGeometry>; } } } export interface ITextMeshProps { text?: string; size?: number; height?: number; color?: string; } export function TextMesh(props: ITextMeshProps) { const { text = 'Test', size = 2, height = 1, color = '#ffffff' } = props; // 用R3F官方loader钩子加载字体,自动处理缓存和销毁 const font = useLoader(FontLoader, monster); // 缓存几何体配置,避免重渲染重复生成几何体 const geometryOptions = React.useMemo(() => ({ font, size, height, curveSegments: 12, bevelEnabled: true, bevelThickness: 0.03, bevelSize: 0.02, bevelOffset: 0, bevelSegments: 5 }), [font, size, height]); return ( <mesh> <textGeometry key={text} args={[text, geometryOptions]} /> {/* 必须添加材质才能正常渲染 */} <meshStandardMaterial color={color} /> </mesh> ); }
App 入口组件
import React from 'react'; import { Canvas } from '@react-three/fiber' import { TextMesh } from './TextMesh'; function App() { return ( <div className='h-screen flex items-center justify-center flex-col bg-orange-300'> <Canvas camera={{ position: [0, 0, 10], fov: 50 }}> {/* 调整光照强度到合理范围,避免文字过暗不可见 */} <ambientLight intensity={0.6} color="white" /> <spotLight color="white" position={[20, 10, 10]} angle={0.3} penumbra={1} intensity={1}/> <TextMesh text="Test" color="#2563eb" /> </Canvas> </div> ); } export default App;
额外注意事项
- 确认依赖版本匹配:
three版本≥0.148.0,@react-three/fiber版本≥8.12.0,避免版本不兼容引发的注册异常 - 不要在模块顶层直接执行
new FontLoader().parse()解析字体,该操作会生成全局副作用,严格模式下极易出现引用失效问题 - 如果不需要自定义字体生成逻辑,也可以直接使用
@react-three/drei提供的<Text>组件,已经封装好了TextGeometry的所有兼容处理,无需手动注册类和加载字体。
内容的提问来源于stack exchange,提问作者afrodos
相关产品推荐
相关产品推荐

