When dragging a node in the brain map, insert a temporary node to show the current inserted position, but it is always stuck and cannot be continued #6509
wangtao-bugkiller posted onGitHub
Describe the bug / 问题描述
脑图里面拖拽节点的时候,去插入一个临时节点来显示当前插入的位置,总是卡死无法继续
Reproduction link / 重现链接
No response
Steps to Reproduce the Bug or Issue / 重现步骤
let dragNodeOriPos, minDisNode, minDisNodeId, dragRect, insertPos, insertIndex, lastInsertPostion, lastMinDisNode, lastMinDisNodeId, targetIndex, newParentId, newParentNode, targetInsertIndex, targetParentID;
/**
检查拖动的节点与其他节点是否发生碰撞
@param {Object} node - 被检查的节点对象
@param {Object} rect - 拖动矩形的属性,包含x、y、width、height
@param {number} x - 拖动矩形的x坐标
@param {number} y - 拖动矩形的y坐标
@returns {boolean|Object} - 如果没有碰撞返回false,如果发生碰撞返回包含插入位置和碰撞状态的对象
/ const collide = (node, rect, x, y) => { // 如果节点不存在,直接返回false if (!node) return false;
// 获取节点的边界框,并添加padding以扩大碰撞检测范围 var nodeBBox = node.getBBox(), padding = 10, expandedNodeBBox = {
x: nodeBBox.minX - padding, y: nodeBBox.minY - padding, width: nodeBBox.width + 2 * padding, height: nodeBBox.height + 2 * padding,
}, // 构造拖动矩形的边界框,高度翻倍以适应特定的碰撞检测需求 draggedRect = {
x: rect.x - padding, y: rect.y - padding, width: rect.width + 2 * padding, height: 2 * rect.height,
};
// 判断拖动过程中是否与其他结点发生碰撞 var isColliding = expandedNodeBBox.x < draggedRect.x + draggedRect.width && expandedNodeBBox.x + expandedNodeBBox.width > draggedRect.x && expandedNodeBBox.y < draggedRect.y + draggedRect.height && expandedNodeBBox.y + expandedNodeBBox.height > draggedRect.y;
// 如果发生碰撞,根据拖动位置确定插入位置 if (isColliding) { var insertPosition =
x > nodeBBox.maxX ? 'right' : x < nodeBBox.maxX && x > nodeBBox.minX ? y > nodeBBox.centerY ? 'bottom' : 'top' : 'left';
// 返回插入位置和碰撞状态 return { insertPos: insertPosition, collide: true }; }
// 如果没有碰撞,返回false return false; };
/**
遍历节点函数
该函数用于在思维导图中找到并返回一个指定的节点及其索引位置
@param {Object} item - 需要查找的节点项,必须包含getID方法以获取节点ID
@param {Function} callback - 回调函数,未在本代码段中使用,但可能为未来扩展预留
@returns {Object} 返回一个对象,包含新父节点和目标索引
/ const traversNodes = (item, callback) => { // 初始化新节点和新索引变量 let newNode, newIndex = 0;
// 获取需要查找的节点的ID const id = item.getID();
// 使用G6工具类的树遍历方法,从保存的思维导图树结构中从上至下查找节点 G6.Util.traverseTreeUp(mindMapTree.save(), function(node, parent, index) { // 当找到节点ID匹配的节点时,保存该节点及其索引,并停止继续遍历 if (node.id === id) {
newNode = node; newIndex = index; return false;
} else {
// 如果当前节点不匹配,继续遍历下一个节点 return true;
} });
// 返回结果对象,包含找到的节点和其索引位置 return { newParentNode: newNode, targetIndex: newIndex, }; };
const traversRootNodes = e => { var found = false, nodeId = e.getID(); // 遍历树的根节点以判断是否为根节点 G6.Util.traverseTree(mindMapTree.save(), function(node, isRoot) { if (!isRoot || node.id !== nodeId) {
found = true; return false;
} }); return found; };
const registerBehavior = () => { G6.registerBehavior('dice-mindmap', { getEvents() {
return { 'node:click': 'clickNode', 'node:dblclick': 'editNode', 'node:mouseenter': 'hoverNode', 'node:mouseleave': 'hoverNodeOut', };
}, clickNode(evt) {
const model = evt.item.get('model'); const name = evt.target.get('action') || ''; const parent = evt.item.get('parent'); const { x, y, images } = model; const image = Array.isArray(images) ? [...images] : []; const point = mindMapTree.getCanvasByPoint(x, y); setNodeContextMenuX(point.x); setNodeContextMenuY(point.y + 40); if (model.id !== currentNode?.get('model')?.id) { setShowDeleteMenu(false); } setCurrentNode(evt); const regex = /previewPic(\d+)/; const match = name.match(regex); const regexDel = /deletePic(\d+)/; const matchDel = name.match(regexDel); if (match) { const urlIndex = parseInt(match[1]); setPicPreviewOpen(true); setPreviewPicUrl(image[urlIndex].url); } if (matchDel) { const delIndex = parseInt(matchDel[1]); image.splice(delIndex, 1); deletePic(evt.item, image); } switch (name) { case 'expand': case 'collapse': mindMapTree.updateItem( evt.item, { collapsed: name === 'expand' ? false : true, }, true ); mindMapTree.setItemState(evt.item, 'collapsed', name === 'expand' ? false : true); mindMapTree.layout(); break; case 'deleteTargetTag': const { id } = evt.target.cfg; setTargetTag(id); setShowDeleteMenu(true); break; default: return; }
}, editNode(evt) {
handleEditNode(evt, true, false);
}, hoverNode(evt) {
try { mindMapTree.setItemState(evt.item, 'hover', true); } catch (error) { console.log(error); }
}, hoverNodeOut(evt) {
try { mindMapTree.setItemState(evt.item, 'hover', false); } catch (error) { console.log(error); }
}, });
G6.registerBehavior('dice-mindmap-drag', { getEvents() {
return { 'node:dragstart': 'handleItemDragStart', 'node:drag': 'handleItemDrag', 'node:dragend': 'handleItemDragEnd', };
},
handleItemDragStart(evt) {
if (!isEditMode()) return; const { item, x, y } = evt; const model = item.get('model'); if (model.id === 'RootNode') { return; } minDisNode = null; dragNodeOriPos = { x: model.x, y: model.y }; const { minX, minY, width, height } = item.getBBox(); dragRect = { deltaX: x - minX, deltaY: y - minY, width, height, }; if (!model.collapsed && model.children && model.children.length > 0) { model.collapsed = true; mindMapTree.setItemState(item, 'collapsed', true); mindMapTree.refreshItem(item); } // mindMapTree.hideItem(item, false); // mindMapTree.refreshPositions();
},
/**
处理思维导图节点拖拽事件
@param {Object} evt - 拖拽事件对象,包含拖拽的相关信息
/ handleItemDrag(evt) { // 获取当前拖拽项的模型信息 const model = evt.item.get('model'); if (model.id === 'RootNode') { return; } // 计算拖拽项的临时位置和中心点坐标 const tempBox = { width: dragRect.width, height: dragRect.height, x: evt.x - dragRect.deltaX, y: evt.y - dragRect.deltaY, centerX: evt.x - dragRect.deltaX + dragRect.width / 2, centerY: evt.y - dragRect.deltaY + dragRect.height / 2, };
// 初始化最近距离节点变量 minDisNode = null;
// 遍历思维导图树,检查拖拽节点与现有节点是否发生碰撞 G6.Util.traverseTreeUp(mindMapTree.save(), function(node) { // 跳过当前拖拽节点和临时节点 if (node.id === model.id || node.id === 'temp') {
return true;
}
// 获取当前遍历的节点对象 const currentNode = mindMapTree.findById(node.id);
// 检查当前节点与拖拽节点是否发生碰撞 const hascollided = collide(currentNode, tempBox, evt.x, evt.y); if (hascollided) {
// 如果发生碰撞,更新最近距离节点及其状态 minDisNode = currentNode; minDisNodeId = node.id; insertPos = hascollided.insertPos; console.log(hascollided, node.title); return false;
} });
// 如果找到最近距离节点,更新其子节点或调整节点位置 if (minDisNode) { const minx = minDisNode.getBBox().minX; mindMapTree.findDataById('temp') && mindMapTree.removeChild('temp', false); const tempNode = {
id: 'temp', type: 'dice-mind-map-temp', title: '',
};
// 根据拖拽位置更新节点或调整节点位置 if (minx <= evt.x) {
if (minDisNodeId === lastMinDisNodeId && lastInsertPostion === insertPos) { return; } mindMapTree.findDataById('temp') && mindMapTree.removeChild('temp', false); const targetNode = mindMapTree.findDataById(minDisNodeId) || { children: [] }; const tagerChild = cloneDeep(targetNode.children || []); tagerChild.push(tempNode); targetParentID = minDisNode.getID(); // if (minDisNode.getModel().collapsed) { // minDisNode.getModel().collapsed = false; // } targetInsertIndex = tagerChild.length - 1; console.log(targetNode.children,tagerChild, 'c',targetInsertIndex); // mindMapTree.updateItem(minDisNodeId,{children: tagerChild},false); // mindMapTree.layout(); mindMapTree.updateChildren(tagerChild, minDisNodeId);
} else {
if (minDisNodeId === lastMinDisNodeId && lastInsertPostion === insertPos) { return; } const traversedNodes = traversNodes(minDisNode); newParentNode = traversedNodes.newParentNode; targetIndex = traversedNodes.targetIndex; newParentId = newParentNode?.id; if (!newParentId) { return; } mindMapTree.findDataById('temp') && mindMapTree.removeChild('temp', false); const childrenArray = cloneDeep(newParentNode.children || []); insertIndex = insertPos === 'top' ? targetIndex : targetIndex + 1; childrenArray.splice(insertIndex, 0, tempNode); console.log(childrenArray, 'p',insertIndex); // mindMapTree.updateItem(newParentId,{children: childrenArray},false); // mindMapTree.layout(); mindMapTree.updateChildren(childrenArray, newParentId); targetParentID = newParentId; targetInsertIndex = insertIndex;
}
// 更新拖拽项的位置 evt.item.updatePosition({
x: dragRect.deltaX, y: dragRect.deltaY,
});
// 更新最近距离节点及其插入位置的缓存 lastMinDisNodeId = minDisNodeId; lastInsertPostion = insertPos; } }, handleItemDragEnd(evt) {}, }); };
G6 Version / G6 版本
4.x
Operating System / 操作系统
macOS
Browser / 浏览器
Chrome
Additional context / 补充说明
No response