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

react-three/fiber搭配OrbitControls正交相机如何约束y轴拖拽物体到x/z平面

问题根因

原有坐标转换逻辑沿用了透视相机的射线计算规则,且依赖全局窗口尺寸而非Canvas实际渲染尺寸,硬编码的z轴距离计算无法适配正交相机的投影特性、角度变化和缩放调整。

修复方案

使用Three.js内置的Raycaster与地面x-z平面求交的方式获取鼠标对应世界坐标,无需手动计算向量适配相机参数,直接替换Obj.jsx代码即可:

import React, { useState, useMemo } from "react";
import { useDrag } from "@use-gesture/react";
import { animated, useSpring } from "@react-spring/three";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";

function Obj({ setIsDragging }) {
  const { camera, size, raycaster } = useThree();
  const [pos, setPos] = useState([0, 1.5, 0]);
  const aspect = size.width / size.height;
  // 定义地面所在的x-z平面,法线向上,平面方程为y=0
  const groundPlane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);

  const [spring, api] = useSpring(() => ({
    position: pos,
    scale: 1,
    rotation: [0, 0, 0],
    config: { friction: 10 }
  }));

  const bind = useDrag(
    ({ active, xy: [x, y], movement: [mx, my] }) => {
      if (active) {
        // 转换为Three.js要求的标准化设备坐标
        const mouse = new THREE.Vector2(
          (x / size.width) * 2 - 1,
          -(y / size.height) * 2 + 1
        );
        // 从相机发出射线
        raycaster.setFromCamera(mouse, camera);
        // 计算射线和地面平面的交点
        const intersectPoint = new THREE.Vector3();
        raycaster.ray.intersectPlane(groundPlane, intersectPoint);
        
        if (intersectPoint.isFinite()) {
          const newPos = [intersectPoint.x, 1.5, intersectPoint.z];
          setPos(newPos);
          api.start({
            position: newPos,
            scale: 1.2,
            rotation: [my / aspect, mx / aspect, 0]
          });
        }
      } else {
        api.start({
          position: pos,
          scale: 1,
          rotation: [0, 0, 0]
        });
      }
      setIsDragging(active);
    }
  );

  return (
    <animated.mesh {...spring} {...bind()} castShadow>
      <dodecahedronBufferGeometry
        castShadow
        attach="geometry"
        args={[1.4, 0]}
      />
      <meshNormalMaterial attach="material" />
    </animated.mesh>
  );
}

export default Obj;

实现说明

  • 内置的射线求交逻辑自动适配正交相机的任意角度、缩放参数,无需额外矫正系数
  • 采用useThree返回的Canvas实际尺寸计算坐标,避免Canvas非全屏时出现坐标偏移
  • 移除了冗余的手动向量计算逻辑,所有坐标转换由Three.js原生方法保证准确性
  • 修复了useState异步更新导致的位置更新滞后问题,计算结果直接同步到弹簧动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 21:06:03