React Three Fiber结合React Spring与Orbit Controls时from值不更新如何解决
解决方案
你的问题核心是useSpring的from配置仅会在组件首次渲染时计算一次,后续用户通过OrbitControls修改相机位置、控制点后,from的值不会自动同步最新状态,导致每次动画都从初始记录的位置启动。
修改后的完整代码如下:
import { useRef, useEffect, useState } from 'react'; import { useThree, useFrame } from '@react-three/fiber'; import { OrbitControls } from '@react-three/drei'; import { useSpring, easings } from '@react-spring/three'; function Controls({ cameraData, duration }) { const [orbit, setOrbit] = useState(true); const [target, setTarget] = useState(cameraData.lookAt); const { gl, camera } = useThree(); // 新增OrbitControls ref,用于获取用户操作后的实时控制点 const controlsRef = useRef(null); // 改用带api的useSpring写法,支持手动触发动画、动态配置参数 const [springProps, api] = useSpring(() => ({ config: { duration: duration ?? 1000, easing: easings.easeCubic }, // 初始配置仅在首次渲染生效 from: { x: camera.position.x, y: camera.position.y, z: camera.position.z, lookAtX: target[0], lookAtY: target[1], lookAtZ: target[2], }, to: { x: cameraData.position[0], y: cameraData.position[1], z: cameraData.position[2], lookAtX: cameraData.lookAt[0], lookAtY: cameraData.lookAt[1], lookAtZ: cameraData.lookAt[2], }, onStart: () => setOrbit(false), onRest: () => { setOrbit(true); setTarget(cameraData.lookAt); }, })); // 监听cameraData变化,每次触发动画前读取当前最新的相机和控制点状态作为from useEffect(() => { api.start({ from: { x: camera.position.x, y: camera.position.y, z: camera.position.z, // 从controls ref拿实时的控制点,不要读取不存在的camera.lookAt属性 lookAtX: controlsRef.current?.target.x ?? target[0], lookAtY: controlsRef.current?.target.y ?? target[1], lookAtZ: controlsRef.current?.target.z ?? target[2], // 如果需要避免起始和结束值一致导致动画不触发,可以给每个from值减0.1,和你原逻辑保持一致 }, to: { x: cameraData.position[0], y: cameraData.position[1], z: cameraData.position[2], lookAtX: cameraData.lookAt[0], lookAtY: cameraData.lookAt[1], lookAtZ: cameraData.lookAt[2], } }); }, [cameraData, api, camera, target]); useFrame(() => { if (!orbit) { // 不要访问内部属性_value,用官方提供的get()方法获取当前弹簧值 camera.position.set( springProps.x.get(), springProps.y.get(), springProps.z.get() ); const lookAtX = springProps.lookAtX.get(); const lookAtY = springProps.lookAtY.get(); const lookAtZ = springProps.lookAtZ.get(); camera.lookAt(lookAtX, lookAtY, lookAtZ); // 同步更新controls的target,避免动画结束后controls跳回旧位置 controlsRef.current?.target.set(lookAtX, lookAtY, lookAtZ); } }); return ( <OrbitControls ref={controlsRef} enabled={orbit} target={target} args={[camera, gl.domElement]} /> ); }
核心修改说明
- 改用
[springProps, api]的useSpring写法,支持手动调用api.start()动态传入动画配置,每次触发动画时都会读取当前相机和控制点的实时状态作为起始值 - 新增
OrbitControls的ref,用来获取用户手动操作后的实时控制点坐标,修复原代码读取不存在的camera.lookAt属性的问题 - 去掉了对
_value内部属性的访问,改用官方提供的get()方法获取弹簧当前值,避免后续react-spring版本更新导致代码失效 - useFrame里同步更新controls的target,避免动画结束后OrbitControls出现位置跳变
内容的提问来源于stack exchange,提问作者tinytree
相关产品推荐
相关产品推荐

