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

在react-three-fiber中点击mesh对象实现相机对焦缩放的方法

实现方案

不需要额外引入第三方依赖,你现有依赖中的OrbitControls配合three.js原生相机API即可实现该功能,也可以直接用项目已安装的react-spring实现平滑的变焦过渡效果。

前置修正

首先修正你现有代码中的小问题:你同时import Box from './Box'又在当前文件定义了同名的Box组件,会导致冲突,保留其中一个即可,以下示例以保留当前文件定义的Box组件为准。

最简实现(无动画直接跳转)

import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import './App.scss';
import { Canvas, useFrame, useThree } from "react-three-fiber";
import { OrbitControls, Stars } from '@react-three/drei';

// 封装相机控制逻辑组件,放在Canvas内部才可获取场景上下文
const CameraController = ({ targetMesh }) => {
  const { camera } = useThree();
  const controlsRef = useRef();

  useEffect(() => {
    if (!targetMesh) return;
    // 获取点击物体的世界坐标
    const meshWorldPos = new THREE.Vector3();
    targetMesh.getWorldPosition(meshWorldPos);
    // 设置轨道控制器的聚焦点为物体坐标
    controlsRef.current.target.copy(meshWorldPos);
    // 定义相机偏移距离,数值越小距离物体越近、变焦倍数越大
    const cameraOffset = 8;
    // 设置相机位置,你也可以根据需求调整偏移方向,不一定是Z轴正方向
    camera.position.set(meshWorldPos.x, meshWorldPos.y, meshWorldPos.z + cameraOffset);
    controlsRef.current.update();
  }, [targetMesh, camera])

  return <OrbitControls ref={controlsRef} />
}

const Box = ({ position, args, color, onMeshClick }) => {
  const mesh = useRef(null);
  return (
    <mesh
      position={position}
      ref={mesh}
      onClick={() => onMeshClick(mesh.current)}
    >
      <boxBufferGeometry attach='geometry' args={args} />
      <meshStandardMaterial attach='material' color={color} />
    </mesh>
  );
}

export default function App() {
  const [selectedMesh, setSelectedMesh] = useState(null);
  return (
    <>
      <Canvas colorManagement>
        <Stars />
        <ambientLight intensity={1.6} />
        <Box 
          position={[2, 0, 0]} 
          args={[3, 3, 2]} 
          color="green" 
          onMeshClick={setSelectedMesh} 
        />
        <Box 
          position={[-2, 0, 0]} 
          args={[1, 2, 1]} 
          color="red" 
          onMeshClick={setSelectedMesh} 
        />
        <CameraController targetMesh={selectedMesh} />
      </Canvas>
    </>
  );
}

平滑过渡变焦实现

如果你需要更流畅的动画效果,可以直接用项目已安装的react-spring实现,仅需修改CameraController组件:

import { useSpring } from '@react-spring/core'

const CameraController = ({ targetMesh }) => {
  const { camera } = useThree();
  const controlsRef = useRef();
  // 定义弹簧动画配置
  const [anim, api] = useSpring(() => ({
    target: [0, 0, 0],
    cameraPos: [0, 0, 10],
    config: { mass: 1, tension: 200, friction: 20 }
  }))

  useEffect(() => {
    if (!targetMesh) return;
    const meshWorldPos = new THREE.Vector3();
    targetMesh.getWorldPosition(meshWorldPos);
    const cameraOffset = 8;
    // 触发动画更新
    api.start({
      target: [meshWorldPos.x, meshWorldPos.y, meshWorldPos.z],
      cameraPos: [meshWorldPos.x, meshWorldPos.y, meshWorldPos.z + cameraOffset]
    })
  }, [targetMesh, api])

  // 每帧更新控制器和相机位置
  useFrame(() => {
    controlsRef.current.target.set(...anim.target.get());
    camera.position.set(...anim.cameraPos.get());
    controlsRef.current.update();
  })

  return <OrbitControls ref={controlsRef} />
}

扩展优化

如果需要适配不同尺寸的物体,保证物体完整显示在视野中,可以通过物体的包围球半径动态计算相机偏移距离:

// 放在useEffect里获取包围球半径
targetMesh.geometry.computeBoundingSphere();
const boundingRadius = targetMesh.geometry.boundingSphere.radius;
// 根据包围球半径和相机视场角计算合适的偏移距离
const cameraOffset = boundingRadius / Math.tan((camera.fov * Math.PI) / 360) * 1.2;

内容的提问来源于stack exchange,提问作者Mir Tanvir Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 07:09:02