3D可视化编辑器模版

体验地址:http://mute.turntip.cn


整个搭建平台核心模块包含如下几个部分:

3D场景渲染

组件拖拽系统

元素编辑功能

状态管理

历史记录与撤销/重做

技术栈

前端框架与库

React 18

用于构建用户界面的JavaScript库

Next.js 14

React框架,提供服务端渲染、路由等功能

TypeScript

静态类型检查的JavaScript超集

Three.js

3D图形库,用于在浏览器中渲染3D场景

React Three Fiber

Three.js的React渲染器

React Three Drei

Three.js的React组件集合

Tailwind CSS

实用优先的CSS框架

Lucide React

现代图标库

状态管理与工具

Zustand

轻量级状态管理库

UUID

用于生成唯一标识符

HTML5 Drag and Drop API

原生拖放功能实现

i3D Editor采用了组件化、模块化的架构设计,主要分为以下几个部分:

架构图

数据流

状态管理

使用Zustand管理应用状态,包括场景元素、选中元素等

用户交互

用户通过界面进行交互,如拖拽组件、选择元素、调整属性等

状态更新

交互触发状态更新,通过Zustand的actions修改状态

UI渲染

状态变化触发UI重新渲染,包括3D场景和编辑界面

历史记录

状态变化被记录到历史栈中,支持撤销/重做操作

核心功能实现

3D场景渲染

i3D Editor使用React Three Fiber和React Three Drei来简化Three.js的使用,实现3D场景的渲染。

bash 复制代码
// Canvas设置
<Canvas
  camera={{ position: viewMode === "3D" ? [5, 5, 5] : [0, 5, 0], fov: 50 }}
  shadows
  className="w-full h-full"
>
  <ambientLight intensity={0.5} />
  <directionalLight position={[10, 10, 10]} intensity={1} castShadow />

  {/* 网格 */}
  <Grid
    args={[100, 100]}
    cellSize={1}
    cellThickness={0.5}
    cellColor="#a0a0ff"
    sectionSize={5}
    sectionThickness={1}
    sectionColor="#2080ff"
    fadeDistance={50}
    fadeStrength={1.5}
    followCamera={false}
    infiniteGrid
  />

  {/* 场景对象 */}
  {elements.map((element) => (
    <SceneObject
      key={element.id}
      element={element}
      isSelected={selectedElement?.id === element.id}
      onClick={() => setSelectedElement(element)}
      viewMode={viewMode}
      activeMode={activeMode}
    />
  ))}

  {/* 控制器 */}
  <OrbitControls makeDefault enabled={!selectedElement || activeMode !== "select"} />

  <Environment preset="studio" />
</Canvas>

组件拖拽系统

i3D Editor实现了一个基于HTML5 Drag and Drop API的拖拽系统,允许用户从组件库拖拽元素到3D场景中。

bash 复制代码
// 拖拽开始
const handleDragStart = (event, elementType) => {
  event.dataTransfer.setData("application/element-type", elementType);
  event.dataTransfer.effectAllowed = "copy";

  // 通知父组件拖拽开始
  onStartDrag(elementType);
};
// 拖拽结束
const handleDrop = (event) => {
  event.preventDefault();

  if (!isDragging || !draggedElementType) return;

  // 获取Canvas容器的位置和尺寸
  const rect = canvasContainerRef.current.getBoundingClientRect();

  // 计算鼠标在Canvas中的相对位置
  const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

  // 创建射线并计算与平面的交点
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(
    new THREE.Vector2(x, y),
    new THREE.PerspectiveCamera(50, rect.width / rect.height, 0.1, 1000)
  );

  const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
  const target = new THREE.Vector3();
  raycaster.ray.intersectPlane(plane, target);

  // 添加元素到场景
  addElement({
    type: draggedElementType,
    position: { x: target.x, y: 0, z: target.z },
    rotation: { x: 0, y: 0, z: 0 },
    scale: 1,
    material: "standard",
    color: getDefaultColor(draggedElementType),
    name: getElementName(draggedElementType),
    displayTip: false,
  });

  // 重置拖拽状态
  setIsDragging(false);
  setDraggedElementType(null);
};

元素编辑功能

i3D Editor提供了浮动工具栏,允许用户对选中的元素进行编辑操作,如移动、旋转、复制和删除等。

bash 复制代码
// 浮动工具栏定位
<div
  ref={toolbarRef}
  className={`absolute bg-white rounded-lg shadow-lg z-20 flex ${isHorizontalLayout ? "flex-row" : "flex-col"} items-center p-2`}
  style={{
    left: `${toolbarPosition.x}px`,
    top: `${toolbarPosition.y}px`,
    transform: "translate(-50%, -120%)",
    transition: "left 0.2s ease, top 0.2s ease",
  }}
>
  {/* 工具按钮 */}
  <button
    className={`p-1.5 rounded-full hover:bg-blue-100 ${activeMode === "select" ? "text-blue-500" : ""}`}
    onClick={() => handleToolClick("select")}
    title="选择工具"
  >
    <MousePointer className="h-4 w-4" />
  </button>

  {/* 更多工具按钮... */}
</div>

状态管理实现

i3D Editor使用Zustand进行状态管理,包括场景元素、选中元素等。

bash 复制代码
// 元素存储
export const useElementStore = create<ElementStore>((set) => ({
  elements: [],
  selectedElement: null,

  addElement: (element) =>
    set((state) => ({
      elements: [...state.elements, { ...element, id: uuidv4() }],
    })),

  updateElement: (id, updates) =>
    set((state) => ({
      elements: state.elements.map((element) => 
        element.id === id ? { ...element, ...updates } : element
      ),
      selectedElement:
        state.selectedElement?.id === id 
          ? { ...state.selectedElement, ...updates } 
          : state.selectedElement,
    })),

  removeElement: (id) =>
    set((state) => ({
      elements: state.elements.filter((element) => element.id !== id),
      selectedElement: state.selectedElement?.id === id ? null : state.selectedElement,
    })),

  setSelectedElement: (element) => set({ selectedElement: element }),

  // 更多actions...
}));

i3D Editor实现了历史记录功能,支持撤销和重做操作。

bash 复制代码
// 历史记录状态
const [history, setHistory] = useState<HistoryState[]>([]);
const [historyIndex, setHistoryIndex] = useState(-1);
const [isUndoRedo, setIsUndoRedo] = useState(false);
// 监听元素变化,更新历史记录
useEffect(() => {
  if (isUndoRedo) {
    setIsUndoRedo(false);
    return;
  }
  if (historyIndex >= 0) {
    const currentState: HistoryState = {
      elements: JSON.parse(JSON.stringify(elements)),
      selectedElementId: selectedElement?.id || null,
    };
    // 检查是否与当前历史记录状态相同
    const lastState = history[historyIndex];
    const isEqual =
      JSON.stringify(lastState.elements) === JSON.stringify(currentState.elements) &&
      lastState.selectedElementId === currentState.selectedElementId;
    if (!isEqual) {
      // 如果在历史记录中间进行了操作,则删除后面的历史记录
      const newHistory = history.slice(0, historyIndex + 1);
      setHistory([...newHistory, currentState]);
      setHistoryIndex(historyIndex + 1);
    }
  }
}, [elements, selectedElement, history, historyIndex]);
// 撤销操作
const handleUndo = () => {
  if (historyIndex > 0) {
    setIsUndoRedo(true);
    const prevState = history[historyIndex - 1];
    // 应用历史状态
    loadElements(prevState.elements);
    // 恢复选中状态
    if (prevState.selectedElementId) {
      const selectedElement = prevState.elements.find(
        (el) => el.id === prevState.selectedElementId
      );
      if (selectedElement) {
        setSelectedElement(selectedElement);
      }
    } else {
      setSelectedElement(null);
    }
    setHistoryIndex(historyIndex - 1);
  }
};
// 重做操作
const handleRedo = () => {
  if (historyIndex &lt; history.length - 1) {
    setIsUndoRedo(true);
    const nextState = history[historyIndex + 1];
    // 应用历史状态
    loadElements(nextState.elements);
    // 恢复选中状态
    if (nextState.selectedElementId) {
      const selectedElement = nextState.elements.find(
        (el) => el.id === nextState.selectedElementId
      );
      if (selectedElement) {
        setSelectedElement(selectedElement);
      }
    } else {
      setSelectedElement(null);
    }
    setHistoryIndex(historyIndex + 1);
  }
};

上面就是核心功能实现,当然项目还有很多需要优化,这里只是给大家提供一个方案思路参考,如果感兴趣可以在github上下载代码学习。

github地址:https://github.com/MrXujiang/3D-Editor

flowmix/docx多模态文档引擎,目前也在持续更新中,欢迎体验参考:https://flowmix.turntip.cn

多维表格 flowmix/mute

体验地址:http://mute.turntip.cn

相关推荐
utf8mb4安全女神13 小时前
HTML网页【vscode】【Linux】
ide·vscode·编辑器
百度搜知知学社13 小时前
一键装裱照片,相框APP内置滤镜与贴纸编辑器
android·编辑器·滤镜·图片编辑·贴纸·相框
趋之15 小时前
Mars3D 三维可视化开发入门实战教程
3d
探物 AI16 小时前
零基础入门3D点云深度学习:从PointNet开始,理解3D数据处理
人工智能·深度学习·3d
码来的小朋友16 小时前
[Python] 制作小游戏创意之3D魔方
python·3d·pygame
zyplayer-doc16 小时前
继飞书之后,又一款知识库推出了开源 Agent-Native CLI 工具
人工智能·pdf·编辑器·飞书·开源软件
伶俜6617 小时前
鸿蒙原生应用实战(一):从零开发一个短视频编辑器 App
编辑器·音视频·harmonyos
山峰哥17 小时前
从全表扫描到覆盖索引:我是怎么干掉慢查询的
数据库·sql·oracle·性能优化·编辑器·深度优先
_Athie17 小时前
【开发工具】自动创建项目文件夹结构
unity·编辑器
pythonpioneer1 天前
PyTorch3D:基于 PyTorch 的高效 3D 深度学习工具库
pytorch·深度学习·其他·3d