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 />);
问题原因
- 重复创建Vector3实例:每次
useFrame执行时都新建THREE.Vector3(6,6,-4),会导致不必要的内存开销和计算波动,可能引发闪烁。 - OrbitControls与自定义动画的冲突:
OrbitControls会持续更新相机状态,当自定义动画启动的第一帧,两者的状态更新可能产生冲突,导致相机位置跳变闪烁。 - 状态切换的帧同步问题:
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
相关产品推荐
相关产品推荐

