React Three Fiber/Drei组件useEffect中useRef为null的问题求助
解决React Three Fiber初始加载时useRef为null的动画触发问题
问题原因
Canvas内部的3D组件是Three.js对象,并非普通DOM元素,其初始化时机晚于React组件的初始渲染周期。普通useEffect依赖cylinderRef.current无法捕获到ref的赋值时机——因为ref的current属性更新不会触发React的依赖重新计算。
解决方案
方法一:使用onCreated回调(推荐)
利用@react-three/fiber组件的onCreated事件,在3D对象创建完成后直接触发动画:
import React, { useRef, Suspense } from 'react'; import { Canvas } from '@react-three/fiber'; import { Cylinder, OrbitControls } from '@react-three/drei'; import gsap from 'gsap'; function Section() { const cylinderRef = useRef(null); const handleMeshCreated = (mesh) => { cylinderRef.current = mesh; // 执行动画 const timeLine = gsap.timeline(); timeLine.to(mesh.position, { y: .7, duration: 3, ease: "power2.out", }); }; return( <Canvas> <Suspense fallback={<></>}> <mesh position={[0,2,0]} onCreated={handleMeshCreated}> <Cylinder scale={[0.9, 0.5, 0.9]}> <meshStandardMaterial color={"#545755"} roughness={0.7}/> </Cylinder> </mesh> <OrbitControls enableZoom={false} autoRotate={false}/> <ambientLight intensity={1}/> <directionalLight position={[3,2,1]}/> </Suspense> </Canvas> ) }
方法二:使用useFrame钩子
通过渲染循环检查ref状态,触发动画后停止监听:
import React, { useRef, useFrame, Suspense } from 'react'; import { Canvas } from '@react-three/fiber'; import { Cylinder, OrbitControls } from '@react-three/drei'; import gsap from 'gsap'; function Section() { const cylinderRef = useRef(null); const animationTriggered = useRef(false); useFrame(() => { if (cylinderRef.current && !animationTriggered.current) { animationTriggered.current = true; const timeLine = gsap.timeline(); timeLine.to(cylinderRef.current.position, { y: .7, duration: 3, ease: "power2.out", }); } }); return( <Canvas> <Suspense fallback={<></>}> <mesh position={[0,2,0]} ref={cylinderRef}> <Cylinder scale={[0.9, 0.5, 0.9]}> <meshStandardMaterial color={"#545755"} roughness={0.7}/> </Cylinder> </mesh> <OrbitControls enableZoom={false} autoRotate={false}/> <ambientLight intensity={1}/> <directionalLight position={[3,2,1]}/> </Suspense> </Canvas> ) }
方法三:结合状态变量监听ref变化
通过状态变量显式触发useEffect,感知ref的赋值:
import React, { useRef, useEffect, useState, Suspense } from 'react'; import { Canvas } from '@react-three/fiber'; import { Cylinder, OrbitControls } from '@react-three/drei'; import gsap from 'gsap'; function Section() { const cylinderRef = useRef(null); const [meshReady, setMeshReady] = useState(false); useEffect(() => { if (meshReady) { const timeLine = gsap.timeline(); timeLine.to(cylinderRef.current.position, { y: .7, duration: 3, ease: "power2.out", }); } }, [meshReady]); return( <Canvas> <Suspense fallback={<></>}> <mesh position={[0,2,0]} ref={(el) => { cylinderRef.current = el; if (el) setMeshReady(true); }}> <Cylinder scale={[0.9, 0.5, 0.9]}> <meshStandardMaterial color={"#545755"} roughness={0.7}/> </Cylinder> </mesh> <OrbitControls enableZoom={false} autoRotate={false}/> <ambientLight intensity={1}/> <directionalLight position={[3,2,1]}/> </Suspense> </Canvas> ) }
内容的提问来源于stack exchange,提问作者Jacob Rita
相关产品推荐
相关产品推荐

