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

使用react-three-fiber实现相机平滑动画问题求助

解决相机平滑过渡到指定Mesh的问题

直接瞬移是因为没有对相机的位置/旋转做插值动画,下面给你两种可行的实现方案:

方案一:用@react-spring/three实现弹簧动画

这是React生态里做3D动画最省心的方式,适配react-three-fiber:

  1. 确保安装依赖:
npm install @react-spring/three
  1. 修改相机组件为动画版,绑定弹簧状态:
import { useSpring, animated } from '@react-spring/three'
import { PerspectiveCamera } from '@react-three/fiber'

// 在组件内部
const [{ position, rotation }, api] = useSpring(() => ({
  position: [0, 0, 5], // 相机初始位置
  rotation: [0, 0, 0],
  config: { duration: 1200 } // 动画时长,可按需调整
}))

// 把普通相机换成animated包装的版本
<animated.perspectiveCamera
  ref={cameraRef}
  position={position}
  rotation={rotation}
  fov={75}
  aspect={window.innerWidth / window.innerHeight}
  near={0.1}
  far={1000}
/>
  1. 找到目标Mesh后触发动画:
const navigateToMesh = (targetMesh) => {
  // 计算相机目标位置(比如在Mesh后方3个单位,避免贴脸)
  const targetPosition = targetMesh.position.clone()
  targetPosition.z += 3

  // 启动弹簧动画
  api.start({
    position: targetPosition.toArray(),
    // 如果需要看向Mesh,也可以给rotation加动画,或者用lookAt结合动画
    // rotation: targetMesh.rotation.toArray()
  })
}

如果需要相机始终看向Mesh,可以在动画里同步更新lookAt,或者用useSpring的onFrame回调实时设置:

api.start({
  position: targetPosition.toArray(),
  onFrame: () => {
    cameraRef.current.lookAt(targetMesh.position)
  }
})

方案二:手动实现缓动动画(无额外依赖)

如果不想加库,用原生requestAnimationFrame做缓动:

  1. 保存必要的引用:
import { useRef, useEffect } from 'react'
import * as THREE from 'three'

const cameraRef = useRef(null)
const targetPosRef = useRef(null)
const isAnimating = useRef(false)
  1. 启动动画循环:
useEffect(() => {
  const animateCamera = () => {
    if (isAnimating.current && targetPosRef.current) {
      const cam = cameraRef.current
      const target = targetPosRef.current
      // 缓动系数,0~1之间,越小动画越慢
      const easeFactor = 0.06

      // 逐帧更新相机位置
      cam.position.x += (target.x - cam.position.x) * easeFactor
      cam.position.y += (target.y - cam.position.y) * easeFactor
      cam.position.z += (target.z - cam.position.z) * easeFactor

      // 当相机距离目标足够近时停止动画
      if (cam.position.distanceTo(target) < 0.02) {
        isAnimating.current = false
        cam.position.copy(target) // 确保最终位置精准
      }

      // 让相机始终看向目标Mesh
      cam.lookAt(targetMeshRef.current.position)
    }
    requestAnimationFrame(animateCamera)
  }

  const frameId = requestAnimationFrame(animateCamera)
  return () => cancelAnimationFrame(frameId)
}, [])
  1. 触发动画:
const goToTargetMesh = (meshId) => {
  const targetMesh = sceneRef.current.getObjectById(meshId)
  if (!targetMesh) return

  // 设置相机目标位置
  targetPosRef.current = targetMesh.position.clone().add(new THREE.Vector3(0, 0, 3))
  isAnimating.current = true
}

注意事项

  • 如果用了OrbitControls,动画期间可以暂时禁用控制器,避免冲突:
const controlsRef = useRef(null)
// 启动动画时
controlsRef.current.enabled = false
// 动画结束后恢复
controlsRef.current.enabled = true

内容的提问来源于stack exchange,提问作者Cyrus Zei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 22:20:26