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

React Three Fiber相机插值动画初始帧闪烁问题求助

相机过渡动画初始闪烁问题修复

问题描述

开发了一个点击按钮触发相机平滑过渡到指定位置的程序,过渡功能正常,但动画初始时相机会出现闪烁现象。当前代码如下:

function CameraAnimation(props) {
  useFrame(state => {
    if (props.started) {
      state.camera.lookAt(0, 2, 0);
      state.camera.position.lerp(new THREE.Vector3(6, 6, -4), .02);
    };
  })

  return null;
}

function App() {
  const [started, setStarted] = React.useState(false);

  return (
    <div>
      <Canvas>
        <mesh>
          <boxGeometry args={[1, 1, 1]} />
        </mesh>
        <OrbitControls />
        <CameraAnimation started={started} />
      </Canvas>
      <button onClick={() => setStarted(!started)}>transition</button>
    </div>
  );
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />);

问题原因

  1. 重复创建Vector3实例:每次useFrame执行时都新建THREE.Vector3(6,6,-4),会导致不必要的内存开销和计算波动,可能引发闪烁。
  2. OrbitControls与自定义动画的冲突:OrbitControls会持续更新相机状态,当自定义动画启动的第一帧,两者的状态更新可能产生冲突,导致相机位置跳变闪烁。
  3. 状态切换的帧同步问题:started状态切换后,第一帧的相机更新可能和之前的状态没有平滑衔接。

修复方案

  • 提前创建目标位置的Vector3实例并缓存,避免重复创建。
  • 在动画启动时禁用OrbitControls,防止状态冲突;动画结束后可重新启用(可选)。
  • 添加动画完成判断,避免持续执行无效的lerp操作。

修复后的代码

import { useState, useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import * as THREE from 'three';

function CameraAnimation(props) {
  // 缓存目标位置,避免每次useFrame创建新实例
  const targetPosition = useRef(new THREE.Vector3(6, 6, -4));
  const targetLookAt = useRef(new THREE.Vector3(0, 2, 0));
  const controls = props.controls;

  useFrame((state) => {
    if (props.started) {
      // 禁用OrbitControls,防止冲突
      if (controls.current) controls.current.enabled = false;
      
      // 平滑过渡位置和朝向
      state.camera.position.lerp(targetPosition.current, 0.02);
      state.camera.lookAt(targetLookAt.current);

      // 判断动画是否完成,完成后可停止或恢复Controls
      if (state.camera.position.distanceTo(targetPosition.current) < 0.01) {
        props.setStarted(false);
        if (controls.current) controls.current.enabled = true;
      }
    }
  });

  return null;
}

function App() {
  const [started, setStarted] = useState(false);
  const controlsRef = useRef(null);

  return (
    <div>
      <Canvas>
        <mesh>
          <boxGeometry args={[1, 1, 1]} />
          <meshStandardMaterial color="gray" />
        </mesh>
        <ambientLight intensity={0.5} />
        <directionalLight position={[5, 5, 5]} />
        {/* 传递OrbitControls的ref给动画组件 */}
        <OrbitControls ref={controlsRef} />
        <CameraAnimation 
          started={started} 
          setStarted={setStarted} 
          controls={controlsRef} 
        />
      </Canvas>
      <button onClick={() => setStarted(true)}>开始过渡</button>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);

关键优化点

  • 使用useRef缓存目标位置的Vector3实例,避免重复创建导致的性能波动。
  • 动画启动时禁用OrbitControls,防止其与自定义相机更新冲突;动画完成后恢复启用,保证用户后续仍可交互。
  • 添加动画完成的判断逻辑,当相机距离目标位置足够近时停止动画,避免无效循环。

内容的提问来源于stack exchange,提问作者Luca Phillips

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:33:17