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

Three.js:如何实现相机朝向平滑过渡至指定向量

嘿,这个场景我太熟悉了!当初做360全景播放器的时候,直接用lookAt()跳转确实会让用户觉得很突兀,完全破坏了沉浸感。咱们可以用**缓动动画(tweening)**来实现平滑过渡,给你两种实操性很强的方案:

方案一:手动实现缓动(无需额外库)

如果不想引入第三方库,完全可以自己在渲染循环里处理插值逻辑,核心思路是逐帧计算相机朝向的过渡进度,用缓动函数让动画更自然(比如开头和结尾慢,中间快)。

  • 先定义几个全局变量保存过渡状态:
let targetLookDir = null; // 目标朝向向量
const transitionDuration = 1000; // 过渡时长(毫秒)
let startTime = null; // 过渡开始时间
  • 点击网格时初始化过渡状态:
mesh.addEventListener('click', () => {
  // 计算相机到网格的朝向向量(归一化后更稳定)
  targetLookDir = mesh.position.clone()
    .sub(camera.position)
    .normalize();
  startTime = performance.now();
});
  • 在渲染循环里处理过渡逻辑:
function render() {
  requestAnimationFrame(render);

  if (targetLookDir && startTime) {
    const elapsed = performance.now() - startTime;
    const progress = Math.min(elapsed / transitionDuration, 1); // 进度 clamp 在 0-1 之间

    // 用 ease-in-out 缓动函数优化过渡曲线,比纯线性更自然
    const easedProgress = progress < 0.5 
      ? 2 * progress * progress 
      : 1 - Math.pow(-2 * progress + 2, 2) / 2;

    // 获取相机当前的朝向向量
    const currentLookDir = camera.getWorldDirection(new THREE.Vector3());
    // 计算当前帧的目标朝向:插值当前朝向和目标朝向
    const currentTarget = new THREE.Vector3().lerpVectors(
      currentLookDir,
      targetLookDir,
      easedProgress
    );

    // 更新相机朝向
    camera.lookAt(camera.position.clone().add(currentTarget));

    // 过渡完成后重置状态
    if (progress >= 1) {
      targetLookDir = null;
      startTime = null;
    }
  }

  renderer.render(scene, camera);
}
方案二:用Tween.js简化开发

如果项目里已经在用动画库,或者不想自己写帧逻辑,Tween.js是Three.js生态里最常用的动画工具,代码会更简洁。

  • 首先引入Tween.js(npm安装的话:npm install @tweenjs/tween.js):
import * as TWEEN from '@tweenjs/tween.js';
  • 点击网格时创建过渡动画:
mesh.addEventListener('click', () => {
  // 计算目标朝向向量
  const targetDir = mesh.position.clone()
    .sub(camera.position)
    .normalize();
  // 获取当前朝向向量,存到临时对象里(Tween需要可修改的属性)
  const currentDir = camera.getWorldDirection(new THREE.Vector3());
  const tweenTarget = { x: currentDir.x, y: currentDir.y, z: currentDir.z };

  // 创建并启动tween动画
  new TWEEN.Tween(tweenTarget)
    .to({ x: targetDir.x, y: targetDir.y, z: targetDir.z }, transitionDuration)
    .easing(TWEEN.Easing.Quadratic.InOut) // 选择缓动曲线
    .onUpdate(() => {
      // 每一帧更新相机朝向
      camera.lookAt(
        camera.position.clone().add(new THREE.Vector3(tweenTarget.x, tweenTarget.y, tweenTarget.z))
      );
    })
    .start();
});
  • 别忘了在渲染循环里更新Tween:
function render() {
  requestAnimationFrame(render);
  TWEEN.update(); // 必须调用这个才能让tween动画运行
  renderer.render(scene, camera);
}
额外注意点
  • 为什么用方向向量而不是直接插值相机的rotation?因为Three.js的欧拉角(rotation)容易出现万向锁问题,导致过渡时出现奇怪的旋转,用方向向量或者四元数(Quaternion)更稳定。如果想用四元数,可以试试THREE.Quaternion.slerp来插值相机的quaternion属性,效果同样出色。
  • 缓动曲线可以根据需求调整:Tween.js提供了很多预设(比如Quintic.InOut、Sinusoidal.InOut),你可以多试几种,找到最贴合你产品风格的过渡效果。
  • 如果你的360播放器里相机位置是固定的,上面的代码直接用就行;要是相机也需要移动,同样的思路可以复用在位置插值上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:49:22