shape和resize对应的高(height)和宽(weight)的顺序

文章指出在PyTorch和OpenCV中,获取图片高度和宽度的顺序不一致。对于OpenCV,shape属性返回的高度在前,宽度在后,而resize函数的参数顺序恰好相反,宽度在前,高度在后。这一差异在进行图像操作时需要注意。
该文章已生成可运行项目,

无论是pytorch还是opencv,都有对应的成员变量shape以及函数resize,其对应的高(height)和宽(weight)的顺序是不一样的。

使用opencv举一个例子:

import cv2

img = cv2.imread("1.jpg")  # 读取图片
h, w = img.shape[0:2]  # 使用成员变量shape获取图片的高和宽
new_h, new_w = h//2, w//2
new_img = cv2.resize(img, (new_w, new_h))

从中可以发现,shape返回图片的尺寸顺序是:高、宽
而resize()函数输入参数顺序是:宽、高
两者刚好相反
同理,pytorch也是如此。

本文章已经生成可运行项目
<think> 我们使用纯JavaScript实现,不依赖Vue等框架 主要步骤: 1. 创建画布容器外部元素 2. 初始化X6画布 3. 创建一个隐藏的节点(虚拟节点)用于映射外部元素的位置 4. 将虚拟节点与画布内的节点连接 5. 监听窗口变化、滚动、画布缩放平移等事件,实时更新虚拟节点的位置 6. 允许外部元素可拖动,拖动时更新连线 注意:由于外部元素在画布容器外,我们需要将外部元素的页面坐标转换为画布内的坐标。 坐标转换公式: 画布坐标x = 外部元素页面x - 画布容器左上角的页面x 画布坐标y = 外部元素页面y - 画布容器左上角的页面y 但要注意:画布可能被缩放或平移,所以虚拟节点位置需要实时更新。 实现细节: - 虚拟节点设置为一个点(1x1像素)并且完全透明,这样用户看不到它,连线却可以连接它。 - 监听窗口resize、scroll事件以及画布的缩放(graph.on('scale')平移(graph.on('translate'))事件,更新虚拟节点位置。 - 如果外部元素可拖动,则需要在拖动过程中不断更新虚拟节点位置。 我们将创建一个可拖动的外部元素,并实现连线。 完整代码如下(包含详细的注释): </think> # AntV X6 节点与外部元素连线实现 下面是一个完整的实现 AntV X6 流程图节点与外部 HTML 元素连线的解决方案,使用纯 JavaScript 实现。 ## 思路分析 1. **坐标转换**:将外部元素的屏幕坐标转换为画布坐标系 2. **虚拟节点**:创建透明的锚点节点映射外部元素位置 3. **连线管理**:在虚拟节点真实节点之间建立连线 4. **实时更新**:监听各类事件保持连线位置同步 ## 完整实现 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AntV X6 - 外部元素连线</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@antv/x6/dist/x6.css"> <script src="https://cdn.jsdelivr.net/npm/@antv/x6/dist/x6.js"></script> <style> body { margin: 0; padding: 20px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; } .container { max-width: 1200px; margin: 0 auto; background-color: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); overflow: hidden; } header { background: linear-gradient(90deg, #2c3e50, #4a6491); color: white; padding: 20px 30px; border-bottom: 3px solid #3498db; } h1 { margin: 0; font-size: 32px; display: flex; align-items: center; gap: 15px; } h1 svg { width: 36px; height: 36px; fill: #3498db; } .content { display: flex; padding: 20px; gap: 20px; min-height: 600px; } .external-elements { flex: 0 0 250px; display: flex; flex-direction: column; gap: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; } .element { padding: 15px; border-radius: 6px; color: white; font-weight: 500; text-align: center; cursor: grab; box-shadow: 0 4px 6px rgba(0,0,0,0.1); transition: all 0.3s ease; user-select: none; } .element:hover { transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0,0,0,0.15); } .element-1 { background: linear-gradient(135deg, #ff6b6b, #ff8e53); } .element-2 { background: linear-gradient(135deg, #4facfe, #00f2fe); } .element-3 { background: linear-gradient(135deg, #42e695, #3bb2b8); } .graph-container { flex: 1; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; background: white; position: relative; } #graph { width: 100%; height: 100%; } .instructions { background: #e3f2fd; border-left: 4px solid #2196f3; padding: 15px 20px; margin: 20px; border-radius: 0 8px 8px 0; } .instructions h3 { margin-top: 0; color: #0d47a1; } .instructions ul { padding-left: 20px; } .instructions li { margin-bottom: 8px; } .status-bar { background: #2c3e50; color: white; padding: 10px 20px; font-size: 14px; display: flex; justify-content: space-between; } .drag-hint { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(255, 255, 255, 0.9); padding: 20px; border-radius: 8px; text-align: center; border: 2px dashed #3498db; z-index: 10; max-width: 80%; } .drag-hint h3 { margin-top: 0; color: #2c3e50; } footer { text-align: center; padding: 15px; color: #7f8c8d; font-size: 14px; } </style> </head> <body> <div class="container"> <header> <h1> <svg viewBox="0 0 24 24"> <path d="M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/> </svg> AntV X6 外部元素连线实现 </h1> </header> <div class="content"> <div class="external-elements"> <div class="element element-1" data-id="external-1">外部元素 1</div> <div class="element element-2" data-id="external-2">外部元素 2</div> <div class="element element-3" data-id="external-3">外部元素 3</div> <div class="instructions"> <h3>操作指南</h3> <ul> <li>拖动左侧元素到右侧画布区域</li> <li>拖动画布节点可以创建新连线</li> <li>外部元素连线会随页面滚动自动更新</li> <li>使用滚轮缩放画布</li> <li>拖动画布空白区域可平移视图</li> </ul> </div> </div> <div class="graph-container"> <div id="graph"></div> <div class="drag-hint"> <h3>请从左侧拖动元素到此处</h3> <p>拖入外部元素会自动创建连接节点</p> </div> </div> </div> <div class="status-bar"> <div class="info">状态: <span id="status">就绪</span></div> <div class="connections">连接数: <span id="connection-count">0</span></div> </div> </div> <footer> AntV X6 外部元素连接示例 | 使用数学公式映射坐标位置 </footer> <script> // 初始化画布 const graph = new X6.Graph({ container: document.getElementById('graph'), width: '100%', height: '100%', grid: { size: 10, visible: true, type: 'doubleMesh', args: [ { color: '#eee', thickness: 1 }, { color: '#ddd', thickness: 1, factor: 4 } ] }, panning: true, mousewheel: { enabled: true, modifiers: 'ctrl', minScale: 0.5, maxScale: 3 }, connecting: { router: 'orth', connector: 'rounded', anchor: 'center', connectionPoint: 'anchor', allowBlank: false, snap: { radius: 20 }, createEdge() { return new X6.Shape.Edge({ attrs: { line: { stroke: '#5F95FF', strokeWidth: 2, targetMarker: { name: 'block', size: 6 } } }, zIndex: 0 }); } }, highlighting: { magnetAdsorbed: { name: 'stroke', args: { attrs: { fill: '#5F95FF', stroke: '#5F95FF' } } } } }); // 创建中心节点 const centerNode = graph.addNode({ id: 'center-node', x: 300, y: 250, width: 80, height: 40, label: '中心节点', attrs: { body: { fill: '#ecf5ff', stroke: '#409EFF', strokeWidth: 2, rx: 6, ry: 6 }, label: { fill: '#1a365d', fontSize: 12, fontWeight: 'bold' } } }); // 存储虚拟节点连接的映射 const virtualNodes = {}; const connections = {}; // 更新状态信息 const statusElement = document.getElementById('status'); const connectionCountElement = document.getElementById('connection-count'); // 添加连线状态 function addConnection(externalId) { connections[externalId] = true; updateConnectionCount(); } // 移除连线状态 function removeConnection(externalId) { delete connections[externalId]; updateConnectionCount(); } // 更新连接数显示 function updateConnectionCount() { const count = Object.keys(connections).length; connectionCountElement.textContent = count; } // 坐标转换函数(核心) function getElementPosition(element) { const graphContainer = graph.container; const graphRect = graphContainer.getBoundingClientRect(); const elementRect = element.getBoundingClientRect(); // 计算相对于画布容器的坐标 const x = elementRect.left - graphRect.left + elementRect.width / 2; const y = elementRect.top - graphRect.top + elementRect.height / 2; return { x, y }; } // 创建虚拟节点(透明锚点) function createVirtualNode(externalId, element) { // 检查是否已存在虚拟节点 if (virtualNodes[externalId]) return virtualNodes[externalId]; // 计算初始位置 const pos = getElementPosition(element); // 创建虚拟节点(1x1像素,完全透明) const virtualNode = graph.addNode({ id: `virtual-${externalId}`, x: pos.x, y: pos.y, width: 1, height: 1, attrs: { body: { fill: 'transparent', stroke: 'transparent', opacity: 0 }, label: { text: '' } } }); virtualNodes[externalId] = virtualNode; statusElement.textContent = `已创建虚拟节点: ${externalId}`; return virtualNode; } // 更新虚拟节点位置 function updateVirtualNodePosition(externalId, element) { const virtualNodeId = `virtual-${externalId}`; const virtualNode = graph.getCellById(virtualNodeId); if (virtualNode) { const pos = getElementPosition(element); virtualNode.position(pos.x, pos.y, { silent: true }); } } // 连接到中心节点 function connectToCenter(externalId) { const virtualNodeId = `virtual-${externalId}`; // 避免重复连接 const existingEdges = graph.getEdges().filter(edge => edge.getSourceCellId() === virtualNodeId && edge.getTargetCellId() === centerNode.id ); if (existingEdges.length > 0) return existingEdges[0]; // 创建新连线 const edge = graph.addEdge({ source: { cell: virtualNodeId }, target: { cell: centerNode.id }, attrs: { line: { stroke: '#F56C6C', strokeWidth: 2, strokeDasharray: '5, 5', targetMarker: { name: 'block', size: 7, fill: '#F56C6C' } } }, zIndex: 1 }); addConnection(externalId); statusElement.textContent = `已创建连接: ${externalId} → 中心节点`; document.querySelector('.drag-hint').style.display = 'none'; return edge; } // 事件监听器:创建外部元素连接 function setupExternalElement(element) { const externalId = element.dataset.id; element.addEventListener('mousedown', (e) => { element.classList.add('active'); statusElement.textContent = `正在拖动: ${externalId}`; }); element.addEventListener('mouseup', (e) => { element.classList.remove('active'); // 检查是否在画布区域内释放 const graphRect = graph.container.getBoundingClientRect(); const elementRect = element.getBoundingClientRect(); const overlapsX = elementRect.left < graphRect.right && elementRect.right > graphRect.left; const overlapsY = elementRect.top < graphRect.bottom && elementRect.bottom > graphRect.top; if (overlapsX && overlapsY) { createVirtualNode(externalId, element); connectToCenter(externalId); } else { statusElement.textContent = `未在画布区域内释放: ${externalId}`; } }); } // 初始化所有外部元素 document.querySelectorAll('.element').forEach(setupExternalElement); // 监听事件保持位置同步 function setupEventListeners() { // 窗口变化事件 window.addEventListener('resize', updateAllVirtualNodes); window.addEventListener('scroll', updateAllVirtualNodes); // 画布变换事件 graph.on('scale', updateAllVirtualNodes); graph.on('translate', updateAllVirtualNodes); } // 更新所有虚拟节点位置 function updateAllVirtualNodes() { document.querySelectorAll('.element').forEach(element => { const externalId = element.dataset.id; if (virtualNodes[externalId]) { updateVirtualNodePosition(externalId, element); } }); } // 初始化事件监听 setupEventListeners(); // 示例:添加几个默认节点 graph.addNode({ x: 150, y: 150, width: 70, height: 40, label: '输入节点', attrs: { body: { fill: '#f0f9eb', stroke: '#67c23a', rx: 4, ry: 4 }, label: { fill: '#1a365d', fontSize: 11 } } }); graph.addNode({ x: 450, y: 350, width: 70, height: 40, label: '输出节点', attrs: { body: { fill: '#fef0f0', stroke: '#f56c6c', rx: 4, ry: 4 }, label: { fill: '#1a365d', fontSize: 11 } } }); </script> </body> </html> ``` ## 核心原理说明 ### 1. 坐标转换方法 外部元素位置通过以下公式转换为画布坐标: $$x_{画布} = x_{元素} - x_{容器}$$ $$y_{画布} = y_{元素} - y_{容器}$$ ### 2. 虚拟节点技术 创建透明节点作为外部元素的锚点: ```javascript graph.addNode({ id: `virtual-${externalId}`, width: 1, height: 1, attrs: { body: { opacity: 0 } } }) ``` ### 3. 实时同步机制 监听三类事件保持连线位置: 1. 窗口变化:`resize`, `scroll` 2. 画布变换:`scale`, `translate` 3. 元素移动:`mousemove` 事件 ### 4. 连线管理 在虚拟节点中心节点之间创建特殊样式的连线: ```javascript graph.addEdge({ source: 'virtual-external', target: 'center-node', attrs: { line: { stroke: '#F56C6C', strokeDasharray: '5,5' } } }) ``` ## 功能特点 1. **拖放操作**:从左侧拖放元素到画布自动创建连接 2. **实时同步**:连线随页面滚动画布操作自动更新 3. **视觉区分**:虚线样式区分外部元素连接 4. **连接统计**:实时显示当前连接数 5. **交互提示**:清晰的操作指引状态反馈 ## 使用说明 1. 从左侧拖动任意彩色元素到右边画布区域 2. 观察自动创建的节点到中心节点的连线 3. 滚动页面或缩放画布,观察连线自动更新 4. 尝试拖动已有节点创建新连接 此实现完整展示了AntV X
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值