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

如何实现Three.js模型上Fabric.js对象的拖拽与缩放功能

实现方案

你已经完成了3D射线检测点选Fabric对象的核心逻辑,拖拽和缩放可以直接基于现有逻辑扩展,以下是可直接落地的实现方案:

1. 拖拽功能实现

核心思路是复用已有的射线检测能力,在拖拽过程中实时同步3D视图点击坐标到Fabric画布坐标:

  • 首先新增3个状态变量,用于标记拖拽状态、当前选中对象、拖拽偏移量
  • 选中对象时记录点击坐标和对象左上角的偏移量,避免拖拽时对象出现跳变
  • 监听鼠标移动事件,拖拽状态下实时获取当前点击位置的UV坐标,转换为画布坐标后更新选中对象的位置,同时同步刷新3D材质纹理

参考代码如下:

// 新增状态变量
let isDragging = false;
let selectedFabricObj = null;
let dragOffset = { x: 0, y: 0 };

// 修改你原有选中逻辑部分,补充偏移量记录
if (selected != null) {
  selectedFabricObj = selected;
  // 计算点击位置和对象左上角的偏移
  const cp = { x: uv.x * 512, y: uv.y * 512 };
  dragOffset.x = cp.x - selected.left;
  dragOffset.y = cp.y - selected.top;
  controls.enabled = false;
  canvas.setActiveObject(selected);
  canvas.renderAll();
} else {
  selectedFabricObj = null;
  canvas.discardActiveObject().renderAll();
  controls.enabled = true;
}

// 新增鼠标事件监听
raycastContainer.addEventListener('mousedown', () => {
  if (selectedFabricObj) isDragging = true;
});

raycastContainer.addEventListener('mousemove', (evt) => {
  if (!isDragging || !selectedFabricObj) return;
  evt.preventDefault();
  // 复用原有射线检测逻辑获取当前UV
  const array = getMousePosition(raycastContainer, evt.clientX, evt.clientY);
  onClickPosition.fromArray(array);
  const intersects = getIntersects(onClickPosition, object.children);
  if (intersects.length > 0 && intersects[0].uv) {
    const uv = intersects[0].uv;
    intersects[0].object.material.map.transformUv(uv);
    // 转换UV为画布坐标,减去偏移量得到对象新位置
    const currentX = uv.x * 512 - dragOffset.x;
    const currentY = uv.y * 512 - dragOffset.y;
    // 更新Fabric对象位置
    selectedFabricObj.set({ left: currentX, top: currentY });
    canvas.renderAll();
    // 同步刷新3D纹理
    intersects[0].object.material.map.needsUpdate = true;
  }
});

raycastContainer.addEventListener('mouseup', () => {
  isDragging = false;
});

2. 缩放功能实现

推荐直接复用Fabric.js原生的控制控件,开发成本最低:

  • 初始化Fabric画布时开启选择控件和控件悬浮层级
  • 监听对象缩放事件,每次缩放后同步更新3D材质即可

参考代码如下:

// 初始化画布时开启控件配置
const canvas = new fabric.Canvas('你的画布ID', {
  selection: true,
  controlsAboveOverlay: true,
  // 保留你原有的其他配置
});

// 监听对象缩放事件,同步更新3D纹理
canvas.on('object:scaling', (e) => {
  const activeObj = e.target;
  if (activeObj) {
    object.children.forEach(mesh => {
      if (mesh.material.map) mesh.material.map.needsUpdate = true;
    });
  }
});

注意事项

  • 拖拽过程中要保持3D轨道控制器处于禁用状态,避免和拖拽操作冲突
  • 如果出现Y轴拖拽方向相反的问题,是因为Three.js UV默认Y轴从下往上,Fabric画布Y轴从上往下,把坐标转换时的uv.y替换为1 - uv.y即可
  • 可补充边界判断逻辑,避免对象被拖拽出画布范围,比如限制left >= 0、top >=0、left + obj.width <= 512、top + obj.height <=512

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 02:45:01