const alignBottomSvg = ``; const alignCenterHorizontallySvg = ``; const alignCenterVerticallySvg = ``; const alignLeftSvg = ``; const alignRightSvg = ``; const alignTopSvg = ``; const horizontalDistributionSvg = ``; const verticalDistributionSvg = ``; const equalWidthSvg = `` const equalHeightSvg = `` const current_node_list = []; // 存储已选中的节点 function getTopNode(selectedNodes) { // 通过比较节点的 y 坐标,找到最上方的节点 return selectedNodes.reduce((topNode, node) => (node.pos[1] < topNode.pos[1] ? node : topNode), selectedNodes[0]); } const ButtonManager = { isInitialized: false, // 检查是否已经初始化 buttonContainer: null, // 保存按钮容器 initialX: 0, // 初始位置X initialY: 0, // 初始位置Y dragStartX: 0, // 拖拽开始时的X dragStartY: 0, // 拖拽开始时的Y isDragging: false, // 检查是否正在拖拽 // 初始化按钮 init() { if (this.isInitialized) return; // 如果已经初始化过,不再重复创建 // 使用 CSS 定义 hover 效果和所有样式 const style = document.createElement('style'); style.textContent = ` #alignment-buttons { position: absolute; top: 20px; right: 10px; left: unset; bottom: unset; display: flex; background: #333333; padding: 4px; border-radius: 4px; z-index: 999; width: 290px; white-space: nowrap; /* 禁止换行,确保按钮在同一行显示 */ } .custom-button { width: 25px; height: 25px; background-size: contain; background-repeat: no-repeat; background-position: center; background-color: #333333; border: none; cursor: pointer; padding: 4px; border-radius: 4px; transition: background-color 0.3s ease; } .custom-button:hover { background-color: #555555; } .divider { width: 2.5px; height: 15px; background-color: #262626; margin: 5px 4px; border-radius: 2.5px; cursor: grab; } .divider:active { cursor: grabbing; } `; document.head.appendChild(style); // 创建按钮容器 this.buttonContainer = document.createElement('div'); this.buttonContainer.id = 'alignment-buttons'; // 从 localStorage 恢复位置 this.restorePosition(); document.body.appendChild(this.buttonContainer); // 创建按钮并分配功能 const buttons = this.getButtons(); buttons.forEach(btn => { if (btn.type === 'divider') { // 创建分割线 const divider = document.createElement('div'); divider.classList.add('divider'); // 添加拖拽功能 divider.addEventListener('mousedown', this.onDragStart.bind(this)); document.addEventListener('mousemove', this.onDragging.bind(this)); document.addEventListener('mouseup', this.onDragEnd.bind(this)); this.buttonContainer.appendChild(divider); } else { // 创建按钮 const button = document.createElement('button'); button.id = btn.id; button.classList.add('custom-button'); button.innerHTML = btn.svg; button.addEventListener('click', btn.action); this.buttonContainer.appendChild(button); } }); this.isInitialized = true; // 标记已经初始化 }, // 获取按钮配置 getButtons() { return [ /* 左对齐 */ { id: 'alignLeft', svg: alignLeftSvg, action: this.alignLeft.bind(this) }, /* 竖向居中对齐 */ { id: 'alignCenterVertically', svg: alignCenterVerticallySvg, action: this.alignCenterVertically.bind(this) }, /* 右对齐 */ { id: 'alignRight', svg: alignRightSvg, action: this.alignRight.bind(this) }, // 分割线 { type: 'divider' }, /* 顶部对齐 */ { id: 'alignTop', svg: alignTopSvg, action: this.alignTop.bind(this) }, /* 横向居中对齐 */ { id: 'alignCenterHorizontally', svg: alignCenterHorizontallySvg, action: this.alignCenterHorizontally.bind(this) }, /* 底部对齐 */ { id: 'alignBottom', svg: alignBottomSvg, action: this.alignBottom.bind(this) }, // 分割线 { type: 'divider' }, /* 横向分布 */ { id: 'horizontalDistribution', svg: horizontalDistributionSvg, action: this.horizontalDistribution.bind(this) }, /* 竖向分布 */ { id: 'verticalDistribution', svg: verticalDistributionSvg, action: this.verticalDistribution.bind(this) }, // 分割线 { type: 'divider' }, /* 等宽 */ { id: 'equalWidth', svg: equalWidthSvg, action: this.equalWidth.bind(this) }, /* 等高 */ { id: 'equalHeight', svg: equalHeightSvg, action: this.equalHeight.bind(this) }, ]; }, // 显示按钮 show() { if (!this.isInitialized) this.init(); // 如果没有初始化,先初始化 this.buttonContainer.style.display = 'flex'; // 显示按钮容器 }, // 隐藏按钮 hide() { if (this.buttonContainer) { this.buttonContainer.style.display = 'none'; // 隐藏按钮容器 } }, // 开始拖拽 onDragStart(e) { if (e.target.classList.contains('divider')) { this.isDragging = true; this.initialX = e.clientX; this.initialY = e.clientY; const rect = this.buttonContainer.getBoundingClientRect(); this.dragStartX = rect.left; this.dragStartY = rect.top; // 锁定宽度和高度,防止拖拽时改变 this.buttonContainer.style.width = `${rect.width}px`; this.buttonContainer.style.height = `${rect.height}px`; } }, // 拖拽中 onDragging(e) { if (this.isDragging) { const deltaX = e.clientX - this.initialX; const deltaY = e.clientY - this.initialY; this.buttonContainer.style.left = `${this.dragStartX + deltaX}px`; this.buttonContainer.style.top = `${this.dragStartY + deltaY}px`; } }, // 结束拖拽 onDragEnd() { if (this.isDragging) { this.isDragging = false; // 获取按钮容器的位置信息 const rect = this.buttonContainer.getBoundingClientRect(); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; // 计算距离四个边的距离 const distanceToTop = rect.top; const distanceToBottom = windowHeight - rect.bottom; const distanceToLeft = rect.left; const distanceToRight = windowWidth - rect.right; // 选择两个最近的边 let top = 'unset'; let bottom = 'unset'; let left = 'unset'; let right = 'unset'; if (distanceToTop < distanceToBottom) { top = `${distanceToTop}px`; } else { bottom = `${distanceToBottom}px`; } if (distanceToLeft < distanceToRight) { left = `${distanceToLeft}px`; } else { right = `${distanceToRight}px`; } // 更新样式使其根据距离四边的情况固定 this.buttonContainer.style.top = top; this.buttonContainer.style.bottom = bottom; this.buttonContainer.style.left = left; this.buttonContainer.style.right = right; // 保存位置到 localStorage localStorage.setItem('buttonContainerPosition', JSON.stringify({ top, left, right, bottom })); // 恢复自动宽度和高度 this.buttonContainer.style.width = 'auto'; this.buttonContainer.style.height = 'auto'; } }, // 恢复位置 restorePosition() { const savedPosition = JSON.parse(localStorage.getItem('buttonContainerPosition')); if (savedPosition) { this.buttonContainer.style.top = savedPosition.top; this.buttonContainer.style.bottom = savedPosition.bottom; this.buttonContainer.style.left = savedPosition.left; this.buttonContainer.style.right = savedPosition.right; } }, // 获取当前选中的节点 getSelectedNodes() { return current_node_list.filter(node => node.is_selected === true); }, // 执行对齐操作的通用方法 alignNodesByProperty(prop, value) { const nodes = this.getSelectedNodes(); if (nodes.length > 0) { nodes.forEach(node => { node.pos[prop] = value; }); LGraphCanvas.active_canvas.setDirty(true, true); // 重新渲染 canvas } }, // 左对齐 alignLeft() { const selectedNodes = this.getSelectedNodes(); if (selectedNodes.length === 1) { selectedNodes[0].pos[0] = 0; } else if (selectedNodes.length > 1) { const leftMost = Math.min(...selectedNodes.map(node => node.pos[0])); this.alignNodesByProperty(0, leftMost); } LGraphCanvas.active_canvas.setDirty(true, true); }, // 右对齐 alignRight() { const rightMost = Math.max(...this.getSelectedNodes().map(node => node.pos[0] + node.size[0])); this.getSelectedNodes().forEach(node => { node.pos[0] = rightMost - node.size[0]; // 右对齐 }); LGraphCanvas.active_canvas.setDirty(true, true); }, // 顶部对齐 alignTop() { const selectedNodes = this.getSelectedNodes(); if (selectedNodes.length === 1) { selectedNodes[0].pos[1] = 0; } else if (selectedNodes.length > 1) { const topMost = Math.min(...selectedNodes.map(node => node.pos[1])); this.alignNodesByProperty(1, topMost); } LGraphCanvas.active_canvas.setDirty(true, true); }, // 底部对齐 alignBottom() { const bottomMost = Math.max(...this.getSelectedNodes().map(node => node.pos[1] + node.size[1])); this.getSelectedNodes().forEach(node => { node.pos[1] = bottomMost - node.size[1]; // 底部对齐 }); LGraphCanvas.active_canvas.setDirty(true, true); }, // 水平居中对齐 alignCenterHorizontally() { const centerY = this.calculateCenter(1); this.getSelectedNodes().forEach(node => { node.pos[1] = centerY - node.size[1] / 2; // 垂直居中 }); LGraphCanvas.active_canvas.setDirty(true, true); }, // 垂直居中对齐 alignCenterVertically() { const centerX = this.calculateCenter(0); this.getSelectedNodes().forEach(node => { node.pos[0] = centerX - node.size[0] / 2; // 水平居中 }); LGraphCanvas.active_canvas.setDirty(true, true); }, // 计算节点的中心坐标 calculateCenter(axis) { const nodes = this.getSelectedNodes(); const min = Math.min(...nodes.map(node => node.pos[axis])); const max = Math.max(...nodes.map(node => node.pos[axis] + node.size[axis])); return (min + max) / 2; }, // 横向分布 horizontalDistribution() { this.distributeNodes(0); }, // 纵向分布 verticalDistribution() { this.distributeNodes(1); }, // 等宽 equalWidth() { this.equalSize(0); }, // 等高 equalHeight() { this.equalSize(1); }, // 通用的节点尺寸调整 equalSize(axis) { const nodes = this.getSelectedNodes(); if (nodes.length > 0) { const averageSize = (nodes.reduce((sum, node) => sum + node.size[axis], 0) / nodes.length).toFixed(2); nodes.forEach(node => { node.size[axis] = parseFloat(averageSize); // 设置所有节点的大小为平均值 }); LGraphCanvas.active_canvas.setDirty(true, true); } }, // 通用的节点分布方法 distributeNodes(axis) { const nodes = this.getSelectedNodes(); if (nodes.length > 1) { // 按照节点在 axis 轴上的位置(高低)进行排序 nodes.sort((a, b) => a.pos[axis] - b.pos[axis]); // 获取最小和最大的位置范围 const min = Math.min(...nodes.map(node => node.pos[axis])); const max = Math.max(...nodes.map(node => node.pos[axis] + node.size[axis])); // 计算节点的总尺寸 const totalSize = nodes.reduce((sum, node) => sum + node.size[axis], 0); // 计算间距 const spacing = (max - min - totalSize) / (nodes.length - 1); // 初始化当前分布位置 let current = min; // 遍历排序后的节点,按顺序分布 nodes.forEach(node => { node.pos[axis] = current; // 设置当前节点位置 current += node.size[axis] + spacing; // 更新下一个节点位置 }); // 重新渲染画布 LGraphCanvas.active_canvas.setDirty(true, true); } } }; function pollForCanvas() { const canvas = document.querySelector('canvas#graph-canvas'); if (canvas) { ButtonManager.show(); // 监听左键单击事件 canvas.addEventListener('click', function (event) { event.preventDefault(); const { offsetX, offsetY } = event; // 获取当前的 `LGraphCanvas` 数据 const current_node = event.target.data?.current_node; // 判断 `current_node` 是否存在 if (current_node !== null && current_node !== undefined) { // 判断 `current_node` 是否已经在 `current_node_list` 中 const nodeExists = current_node_list.some(node => node.id === current_node.id); if (!nodeExists) { // 如果当前节点不存在列表中,则添加 current_node_list.push(current_node); } } else { // 如果没有选中的节点,清空列表 current_node_list.length = 0; } // const selectedNodes = current_node_list.filter(node => node.is_selected === true); }); } else { setTimeout(pollForCanvas, 1000); // 每隔 1 秒尝试查找一次 } } // 启动轮询查找 pollForCanvas();