我正在制作一个脚本,使用 SVG 根据以下形式的输入绘制有向图
let data = {
'A': {
'children': ['B', 'C'],
'parents': [],
'coords': {
'x': 10,
'y': 10
}
},
'B': {
'children': ['C'],
'parents': ['A'],
'coords': {
'x': 30,
'y': 10
}
},
'C': {
'children': [],
'parents': ['A', 'B'],
'coords': {
'x': 20,
'y': 20
}
}
}
它使用三次贝塞尔曲线在父节点和子节点之间创建路径。这个想法是能够根据每个节点的“坐标”属性构建图形的可视化,然后允许用户通过拖放来实时移动节点。
我实现得很好,直到我添加了平移和缩放功能。现在,如果图像被平移和/或缩放,当我将元素的位置更新到光标位置时,它们会被放置在错误的位置。这是我目前必须更新位置的拖动功能
function startDrag(evt) {
if (evt.target.classList.contains('draggable')) {
selectedElement = evt.target;
// we need to store the IDs of paths connecting to the nodes so that we can update their positions accordingly
// Their IDs are stored as `${parent_key}_to_${child_key}`, e.g., #A_to_I
path_ids = [];
let node_key = selectedElement.getAttributeNS(null, 'id');
for (let child_key of data[node_key]['children']) {
path_ids.push(`${node_key}_to_${child_key}`);
}
for (let parent_key of data[node_key]['parents']) {
path_ids.push(`${parent_key}_to_${node_key}`);
}
}
}
function drag(evt) {
if (selectedElement) {
evt.preventDefault();
// we need zoom/pan information to reposition dragged nodes correctly
///////////////////////////////////////////////////
// Potentially use some of this data to calculate correct positions ???
let matrix = document.getElementById('scene').getAttributeNS(null, 'transform');
let m = matrix.slice(7, matrix.length-1).split(' ');
let zoomFactor = m[0];
let panX = m[4];
let panY = m[5];
let svgBBox = svg.getBBox();
///////////////////////////////////////////////////
// move the node itself
selectedElement.setAttributeNS(null, 'cx', evt.clientX);
selectedElement.setAttributeNS(null, 'cy', evt.clientY);
// now for each path connected to the node, we need to update either the first vertex of the cubic bezier curve, or the final vertex
// if id is ${clicked_node}_to_${other} then we change the first point, if it is ${other}_to_${clicked_node} then the last node
let clicked_node = selectedElement.getAttributeNS(null, 'id');
for (let path_id of path_ids) {
let path = document.getElementById(path_id);
let bez_d = path.getAttributeNS(null, 'd');
let bez_split = bez_d.split(' ');
if (path_id[0] === clicked_node) {
let new_d = `M ${evt.clientX} ${evt.clientY} C ${evt.clientX},${evt.clientY}`;
new_d += ` ${bez_split[5]} ${bez_split[6]}`;
path.setAttributeNS(null, 'd', new_d);
} else if (path_id[path_id.length - 1] === clicked_node) {
let new_d = `M ${bez_split[1]} ${bez_split[2]} C ${bez_split[4]} ${bez_split[5]}`;
new_d += ` ${evt.clientX},${evt.clientY}`;
path.setAttributeNS(null, 'd', new_d);
}
}
}
}
function endDrag(evt) {
selectedElement = null;
path_ids = [];
}
正如您在 Drag() 函数中看到的,我能够在平移/缩放后从 svg 本身获取 bbox 数据,并且我能够获取容纳所有可拖动节点的
请参阅 https://jsfiddle.net/quamjxg7/ 获取完整代码。
简单地说:更新可拖动 SVG 元素的位置时如何考虑平移和缩放?
哇,这个解决方案可以说是微不足道的。
我意识到我可以通过
function drag(evt) {
if (selectedElement) {
evt.preventDefault();
// we need zoom/pan information to reposition dragged nodes correctly
let matrix = document.getElementById('scene').getAttributeNS(null, 'transform');
let m = matrix.slice(7, matrix.length-1).split(' ');
let zoomFactor = m[0];
let panX = m[4];
let panY = m[5];
let newX = (evt.clientX - panX)/zoomFactor;
let newY = (evt.clientY - panY)/zoomFactor;
// move the node itself
selectedElement.setAttributeNS(null, 'cx', newX);
selectedElement.setAttributeNS(null, 'cy', newY);
// now for each path connected to the node, we need to update either the first vertex of the cubic bezier curve, or the final vertex
// if id is ${clicked_node}_to_${other} then we change the first point, if it is ${other}_to_${clicked_node} then the last node
let clicked_node = selectedElement.getAttributeNS(null, 'id');
for (let path_id of path_ids) {
let path = document.getElementById(path_id);
let bez_d = path.getAttributeNS(null, 'd');
let bez_split = bez_d.split(' ');
if (path_id[0] === clicked_node) {
let new_d = `M ${newX} ${newY} C ${newX},${newY}`;
new_d += ` ${bez_split[5]} ${bez_split[6]}`;
path.setAttributeNS(null, 'd', new_d);
} else if (path_id[path_id.length - 1] === clicked_node) {
let new_d = `M ${bez_split[1]} ${bez_split[2]} C ${bez_split[4]} ${bez_split[5]}`;
new_d += ` ${newX},${newY}`;
path.setAttributeNS(null, 'd', new_d);
}
}
}
}
已实施修复的完整代码示例 https://jsfiddle.net/x2e1wrLg/