Kaynağa Gözat

抽象逻辑

yichael 4 ay önce
ebeveyn
işleme
b131fbcec4

+ 24 - 57
src/pages/blueprint/canvas/canvas.js

@@ -5,6 +5,12 @@
 import { useEffect, useRef, useState } from 'react';
 import { initCanvasController } from '../utils/canvas-controller.js';
 import { ConnectionManager } from '../utils/connection-manager.js';
+import { 
+  useNodeDrag, 
+  handleNodeDragStart, 
+  handleNodeDragMove, 
+  handleNodeDragEnd 
+} from '../node-renderer/node-renderer.js';
 
 /**
  * 画布逻辑钩子
@@ -20,12 +26,17 @@ export function useCanvasLogic(connections, externalControllerRef, nodes = []) {
   const [transform, setTransform] = useState({ scale: 1, translateX: 0, translateY: 0 });
   const [connectingStart, setConnectingStart] = useState(null);
   const [connectingEnd, setConnectingEnd] = useState(null);
-  const [draggedNodeId, setDraggedNodeId] = useState(null);
-  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
   
-  // 使用 ref 来存储拖拽状态,避免闭包陷阱
-  const draggedNodeIdRef = useRef(null);
-  const dragOffsetRef = useRef({ x: 0, y: 0 });
+  // 使用节点拖拽管理 hook(集中管理拖拽逻辑)
+  const {
+    draggedNodeId,
+    dragOffset,
+    startDrag,
+    endDrag,
+    isDragging,
+    getDraggedNodeId,
+    getDragOffset
+  } = useNodeDrag();
   
   // 初始化画布控制器
   useEffect(() => {
@@ -367,46 +378,19 @@ export function useCanvasLogic(connections, externalControllerRef, nodes = []) {
   
   /**
    * 处理节点鼠标按下(开始拖拽)
+   * 使用 node-renderer.js 中的统一逻辑
    */
   const handleNodeMouseDown = (e, nodeId, nodes, selectedNodeIds, onNodeSelect) => {
-    if (e.button !== 0) return;
-    
     const node = nodes.find(n => n.id === nodeId);
     if (!node) return;
     
     const rect = canvasRef.current.getBoundingClientRect();
-    const canvasX = (e.clientX - rect.left - transform.translateX) / transform.scale;
-    const canvasY = (e.clientY - rect.top - transform.translateY) / transform.scale;
-    
-    const offset = {
-      x: canvasX - node.x,
-      y: canvasY - node.y
-    };
-    
-    // 同时更新 state 和 ref,确保所有闭包都能访问到最新值
-    setDraggedNodeId(nodeId);
-    setDragOffset(offset);
-    draggedNodeIdRef.current = nodeId;
-    dragOffsetRef.current = offset;
-    
-    // 将 cursor 应用到 document.body,确保在边界外也保持 grabbing
-    document.body.style.cursor = 'grabbing';
-    document.body.style.userSelect = 'none';
-    
-    if (e.ctrlKey || e.metaKey) {
-      onNodeSelect?.(nodeId, true);
-    } else {
-      if (!selectedNodeIds.includes(nodeId)) {
-        onNodeSelect?.(nodeId, false);
-      }
-    }
-    
-    e.preventDefault();
-    e.stopPropagation();
+    handleNodeDragStart(e, node, rect, transform, startDrag, onNodeSelect, selectedNodeIds);
   };
   
   /**
    * 处理鼠标移动(连线预览、节点拖拽)
+   * 使用 node-renderer.js 中的统一逻辑
    */
   const handleMouseMove = (e, onNodeMove) => {
     const rect = canvasRef.current.getBoundingClientRect();
@@ -422,34 +406,17 @@ export function useCanvasLogic(connections, externalControllerRef, nodes = []) {
       setConnectingEnd({ x, y });
     }
     
-    // 使用 ref 来检查拖拽状态,避免闭包问题
-    if (draggedNodeIdRef.current) {
-      // 计算节点在画布坐标系中的新位置
-      const containerX = e.clientX - rect.left;
-      const containerY = e.clientY - rect.top;
-      const canvasX = (containerX - transform.translateX) / transform.scale - dragOffsetRef.current.x;
-      const canvasY = (containerY - transform.translateY) / transform.scale - dragOffsetRef.current.y;
-      
-      onNodeMove?.(draggedNodeIdRef.current, canvasX, canvasY);
-    }
+    // 使用 node-renderer.js 中的拖拽处理逻辑
+    handleNodeDragMove(e, rect, transform, getDraggedNodeId, getDragOffset, onNodeMove);
   };
   
   /**
    * 处理鼠标释放
+   * 使用 node-renderer.js 中的统一逻辑
    */
   const handleMouseUp = (e) => {
-    // 使用 ref 来检查拖拽状态,避免闭包问题
-    if (draggedNodeIdRef.current) {
-      // 恢复 document.body 的 cursor 样式
-      document.body.style.cursor = '';
-      document.body.style.userSelect = '';
-      
-      // 同时清除 state 和 ref
-      setDraggedNodeId(null);
-      setDragOffset({ x: 0, y: 0 });
-      draggedNodeIdRef.current = null;
-      dragOffsetRef.current = { x: 0, y: 0 };
-    }
+    // 使用 node-renderer.js 中的拖拽结束逻辑
+    handleNodeDragEnd(endDrag);
     
     // 如果正在连线但没有连接到端口,取消连线
     if (connectingStart) {

+ 142 - 0
src/pages/blueprint/node-renderer/node-renderer.js

@@ -2,6 +2,148 @@
  * 节点渲染逻辑
  */
 
+import { useRef, useState } from 'react';
+
+/**
+ * 节点拖拽管理 Hook
+ * 集中管理节点拖拽的状态和逻辑
+ */
+export function useNodeDrag() {
+  const [draggedNodeId, setDraggedNodeId] = useState(null);
+  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
+  
+  // 使用 ref 来存储拖拽状态,避免闭包陷阱
+  const draggedNodeIdRef = useRef(null);
+  const dragOffsetRef = useRef({ x: 0, y: 0 });
+  
+  /**
+   * 开始拖拽节点
+   */
+  const startDrag = (nodeId, offset) => {
+    setDraggedNodeId(nodeId);
+    setDragOffset(offset);
+    draggedNodeIdRef.current = nodeId;
+    dragOffsetRef.current = offset;
+    
+    // 将 cursor 应用到 document.body,确保在边界外也保持 grabbing
+    document.body.style.cursor = 'grabbing';
+    document.body.style.userSelect = 'none';
+  };
+  
+  /**
+   * 结束拖拽节点
+   */
+  const endDrag = () => {
+    if (draggedNodeIdRef.current) {
+      // 恢复 document.body 的 cursor 样式
+      document.body.style.cursor = '';
+      document.body.style.userSelect = '';
+      
+      // 同时清除 state 和 ref
+      setDraggedNodeId(null);
+      setDragOffset({ x: 0, y: 0 });
+      draggedNodeIdRef.current = null;
+      dragOffsetRef.current = { x: 0, y: 0 };
+    }
+  };
+  
+  /**
+   * 检查是否正在拖拽
+   */
+  const isDragging = () => {
+    return draggedNodeIdRef.current !== null;
+  };
+  
+  /**
+   * 获取当前拖拽的节点ID
+   */
+  const getDraggedNodeId = () => {
+    return draggedNodeIdRef.current;
+  };
+  
+  /**
+   * 获取当前拖拽偏移量
+   */
+  const getDragOffset = () => {
+    return dragOffsetRef.current;
+  };
+  
+  return {
+    draggedNodeId,
+    dragOffset,
+    startDrag,
+    endDrag,
+    isDragging,
+    getDraggedNodeId,
+    getDragOffset
+  };
+}
+
+/**
+ * 处理节点拖拽开始
+ * @param {Event} e - 鼠标事件
+ * @param {Object} node - 节点对象
+ * @param {Object} canvasRect - 画布矩形区域
+ * @param {Object} transform - 画布变换(scale, translateX, translateY)
+ * @param {Function} startDrag - 开始拖拽回调
+ * @param {Function} onNodeSelect - 节点选择回调
+ * @param {Array} selectedNodeIds - 已选中的节点ID列表
+ */
+export function handleNodeDragStart(e, node, canvasRect, transform, startDrag, onNodeSelect, selectedNodeIds) {
+  if (e.button !== 0) return;
+  
+  const canvasX = (e.clientX - canvasRect.left - transform.translateX) / transform.scale;
+  const canvasY = (e.clientY - canvasRect.top - transform.translateY) / transform.scale;
+  
+  const offset = {
+    x: canvasX - node.x,
+    y: canvasY - node.y
+  };
+  
+  startDrag(node.id, offset);
+  
+  if (e.ctrlKey || e.metaKey) {
+    onNodeSelect?.(node.id, true);
+  } else {
+    if (!selectedNodeIds.includes(node.id)) {
+      onNodeSelect?.(node.id, false);
+    }
+  }
+  
+  e.preventDefault();
+  e.stopPropagation();
+}
+
+/**
+ * 处理节点拖拽移动
+ * @param {Event} e - 鼠标事件
+ * @param {Object} canvasRect - 画布矩形区域
+ * @param {Object} transform - 画布变换
+ * @param {Function} getDraggedNodeId - 获取拖拽节点ID函数
+ * @param {Function} getDragOffset - 获取拖拽偏移量函数
+ * @param {Function} onNodeMove - 节点移动回调
+ */
+export function handleNodeDragMove(e, canvasRect, transform, getDraggedNodeId, getDragOffset, onNodeMove) {
+  const draggedNodeId = getDraggedNodeId();
+  if (!draggedNodeId) return;
+  
+  const dragOffset = getDragOffset();
+  const containerX = e.clientX - canvasRect.left;
+  const containerY = e.clientY - canvasRect.top;
+  const canvasX = (containerX - transform.translateX) / transform.scale - dragOffset.x;
+  const canvasY = (containerY - transform.translateY) / transform.scale - dragOffset.y;
+  
+  onNodeMove?.(draggedNodeId, canvasX, canvasY);
+}
+
+/**
+ * 处理节点拖拽结束
+ * @param {Function} endDrag - 结束拖拽回调
+ */
+export function handleNodeDragEnd(endDrag) {
+  endDrag();
+}
+
 /**
  * 计算节点高度(根据端口数量)
  * @param {Object} node - 节点对象

+ 2 - 2
static/processing/测试/bp.json

@@ -13,8 +13,8 @@
 			"y": 360
 		},
 		"var_set_swipeDirection": {
-			"x": 580,
-			"y": 320
+			"x": 620,
+			"y": 260
 		}
 	}
 }