React中Three.js如何在useEffect外部使用变量数据及动画函数
React中Three.js动画函数外部调用及变量共享方案
核心思路
用useRef存储Three.js核心实例(场景、相机、渲染器)、动画ID以及需要跨函数共享的变量——useRef的.current属性会始终保持最新值,且不会触发组件重渲染,完美适配动画循环和外部调用的需求。
代码实现
import React, { useEffect, useRef } from 'react'; import * as THREE from 'three'; function ThreeScene() { // 存储Three.js核心实例 const sceneRef = useRef(null); const cameraRef = useRef(null); const rendererRef = useRef(null); const animationIdRef = useRef(null); // 存储需要跨函数共享的动态变量 const rotationSpeedRef = useRef(0.01); // 定义动画函数,直接读取ref中的最新值 const animate = () => { animationIdRef.current = requestAnimationFrame(animate); // 使用ref.current获取最新变量,驱动场景变化 sceneRef.current.rotation.y += rotationSpeedRef.current; rendererRef.current.render(sceneRef.current, cameraRef.current); }; useEffect(() => { // 初始化Three.js场景 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 添加测试立方体 const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); camera.position.z = 5; // 将实例存入ref,供外部和动画函数访问 sceneRef.current = scene; cameraRef.current = camera; rendererRef.current = renderer; // 启动初始动画循环 animate(); // 组件卸载时清理资源 return () => { cancelAnimationFrame(animationIdRef.current); renderer.domElement.remove(); }; }, []); // 外部调用动画的示例:比如按钮点击更新变量并重启动画 const adjustAnimation = () => { // 先取消当前动画帧 if (animationIdRef.current) { cancelAnimationFrame(animationIdRef.current); } // 更新共享变量的最新值 rotationSpeedRef.current = rotationSpeedRef.current === 0.01 ? 0.03 : 0.01; // 重新启动动画 animate(); }; return ( <div> <button onClick={adjustAnimation}>切换动画速度</button> </div> ); } export default ThreeScene;
关键说明
- 所有需要在动画函数和外部函数中共享的内容,都存入
useRef,确保每次访问都是最新值,不会因组件重渲染丢失状态。 - 动画函数无需额外包裹
useCallback,因为它依赖的是useRef的.current属性,不受组件重渲染影响。 - 外部调用时,直接通过
ref.current访问变量或调用动画函数即可,比如示例中adjustAnimation函数既更新了速度变量,又重新启动了动画。 - 务必在组件卸载时取消动画帧,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Gowtham S
相关产品推荐
相关产品推荐

