如何在React Three Fiber中不使用lookAt()实现相机动画?
解决React Three Fiber相机动画弹跳问题
问题根源
你遇到的弹跳现象,是因为相机位置通过lerp平滑过渡,但lookAt的目标点是突然切换的(从模型原位置直接跳到Z+2的位置)。相机每帧都要朝向一个突变的目标,导致朝向动画与位置动画不同步,产生抖动弹跳。
解决方案
让相机的朝向目标点也跟着平滑过渡,和相机位置的动画速率保持一致,确保位置和朝向的动画同步执行。
修改后的代码
import { useAnimations, useGLTF } from '@react-three/drei' import { useEffect, useRef } from 'react' import { useControls, button} from 'leva' import { useFrame } from '@react-three/fiber' import { Vector3 } from 'three' export default function Character ({isContact}) { const character = useGLTF('./model.gltf') const { actions } = useAnimations(character.animations, character.scene) const markerRef = useRef() const cameraPos = new Vector3() // 定义两种状态下的朝向目标点 const targetContact = new Vector3() const targetDefault = new Vector3() // 用于平滑过渡的当前朝向目标 const currentTarget = useRef(new Vector3()) // 初始化目标点(模型位置固定时只需执行一次) useEffect(() => { if (!markerRef.current) return const pos = markerRef.current.position // 联系状态下的朝向目标(Z轴偏移+2) targetContact.set(pos.x, pos.y + 1, pos.z + 2) // 默认状态下的朝向目标 targetDefault.set(pos.x, pos.y + 1, pos.z) // 初始朝向默认目标 currentTarget.current.copy(targetDefault) }, []) useFrame((state) => { if (isContact) { // 平滑过渡相机位置到联系状态 cameraPos.set(-4, 0, 0) // 平滑过渡朝向目标到联系状态的点 currentTarget.current.lerp(targetContact, 0.01) } else { // 平滑过渡相机位置到默认状态 cameraPos.set(-3, 1.5, 4) // 平滑过渡朝向目标到默认状态的点 currentTarget.current.lerp(targetDefault, 0.01) } // 更新相机位置 state.camera.position.lerp(cameraPos, 0.01) // 朝向平滑过渡后的目标点 state.camera.lookAt(currentTarget.current) // 注意:updateProjectionMatrix 仅在修改相机投影参数(如fov、aspect)时需要,此处可移除 }) return ( <primitive object={character.scene} position-y={-1.2} ref={markerRef} /> ) }
关键修改点
- 分离目标点与位置动画:定义两种状态的朝向目标点,并用
currentTarget作为中间变量实现平滑过渡,避免目标点突变。 - 同步动画速率:相机位置和朝向目标的
lerp使用相同的速率(0.01),确保两者动画同步。 - 移除不必要的
updateProjectionMatrix:仅当相机的投影参数(如视野、宽高比)变化时才需要调用该方法,移动位置/朝向时不需要。
内容的提问来源于stack exchange,提问作者lorenzoajt
相关产品推荐
相关产品推荐

