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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 04:10:35