D3用一条线连接来自不同组的矩形
d3 connecting rectangles from different groups by a line
我正在尝试连接来自不同组的两个矩形,但是组可以拖动。如果群是静态的,就能做功。但是当我拖动大的组时,线条的行为就变得混乱了。我想点击一个圆圈并开始绘制线条,当鼠标移动时移动线条,然后我想点击另一个圆圈并结束线条。这是我干活用的小提琴,有人能帮我一把吗?
<g id="a" transform="translate(0,0)">
<g>
<rect x="10" y="10" width="200" height="200" fill="red"></rect>
<circle r="5" cx="10" cy="105" fill="blue"></circle>
<circle r="5" cx="210" cy="105" fill="blue"></circle>
<line id="lineOne" x1="210" y1="105" x2="200" y2="200" style="stroke: green; stroke-width: 3px"></line>
</g>
<g id="b" class="e">
<rect x="20" y="20" width="50" height="50" fill="black"></rect>
<circle r="5" cx="20" cy="45" fill="blue"></circle>
<circle r="5" cx="70" cy="45" fill="blue"></circle>
</g>
<g id="c" class="e">
<rect x="90" y="20" width="50" height="50" fill="black"></rect>
<circle r="5" cx="90" cy="45" fill="blue"></circle>
<circle r="5" cx="140" cy="45" fill="blue"></circle>
</g>
</g>
<script>
d3.select('svg').on('mousemove', function () {
d3.select('#lineOne').attr('x2', d3.mouse(this)[0]).attr('y2', d3.mouse(this)[1]);
})
;
d3.select('#a').call(d3.behavior.drag()
.origin(function () {
var t = d3.select(this);
return {
x: t.attr("x") + d3.transform(t.attr("transform")).translate[0],
y: t.attr("y") + d3.transform(t.attr("transform")).translate[1]
};
})
.on('drag', function () {
d3.select(this).attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
}));
d3.selectAll('.e')
.call(d3.behavior.drag()
.origin(function () {
var t = d3.select(this);
return {
x: t.attr("x") + d3.transform(t.attr("transform")).translate[0],
y: t.attr("y") + d3.transform(t.attr("transform")).translate[1]
};
}
)
.on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
})
.on('drag', function () {
var parent = d3.select('#a').select('rect');
var current = d3.select(this).select('rect');
var dx = d3.event.x;
var dy = d3.event.y;
var x, y;
if (((Number(current.attr('x')) + dx) > Number(parent.attr('width')))) {
console.log('x case');
x = Number(parent.attr('width')) - Number(current.attr('width')) - Number(current.attr('x'));
}
else if (dx < 0 && (Number(current.attr('x')) - Number(parent.attr('x')) + dx ) < 0) {
x = -1 * Number(current.attr('x')) + 20;
}
else {
x = dx;
}
if (((Number(current.attr('y')) + dy) > Number(parent.attr('height')))) {
y = Number(parent.attr('height')) - Number(current.attr('height')) - Number(current.attr('y'));
}
else if (dy < 0 && (Number(current.attr('y')) - Number(parent.attr('y')) + dy ) < 0) {
y = -1 * Number(current.attr('y')) + 20;
}
else {
y = dy;
}
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')');
}))
.on('dragend', function () {
d3.select(this).call(d3.behavior.drag().origin(function () {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
}))
})
;
</script>
在拖动矩形时也必须更新链接。用变换矩阵计算x1,x2,y1, y2的位置。我已经创建了一个工作代码片段。有关更多细节,请参考代码。如有疑问,请尽管提。
d3.selectAll(".line")[0]
.forEach(function(el) {
var d = d3.select(el).data()[0],
line = d3.select(el);
if (d.src == g.attr("id")) {
var pt = svg.createSVGPoint();
pt.x = parseInt(g.select("circle." + d.sPos).attr("cx"));
pt.y = parseInt(g.select("circle." + d.sPos).attr("cy"));
pt = pt.matrixTransform(g.node().getCTM());
pt = pt.matrixTransform(d3.select("#a").node().getCTM().inverse());
line.attr('x1', pt.x).attr('y1', pt.y);
} else if (d.tgt == g.attr("id")) {
var pt = svg.createSVGPoint();
pt.x = parseInt(g.select("circle." + d.ePos).attr("cx"));
pt.y = parseInt(g.select("circle." + d.ePos).attr("cy"));
pt = pt.matrixTransform(g.node().getCTM());
pt = pt.matrixTransform(d3.select("#a").node().getCTM().inverse());
line.attr('x2', pt.x)
.attr('y2', pt.y);
}
});
var svg = d3.select('svg').node();
d3.select('svg').on('mousemove', function() {
if (dragging) {
var rad = 5;
var x = d3.mouse(d3.select('#a').node())[0],
y = d3.mouse(d3.select('#a').node())[1];
d3.select('#dummyPath').attr('x2', x - rad).attr('y2', y - rad);
d3.select('#dummyPath').style("display", "block")
}
});
d3.select('#a').call(d3.behavior.drag()
.origin(function() {
var t = d3.select(this);
return {
x: t.attr("x") + d3.transform(t.attr("transform")).translate[0],
y: t.attr("y") + d3.transform(t.attr("transform")).translate[1]
};
})
.on('drag', function() {
d3.select(this).attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
}));
d3.selectAll('.e')
.call(d3.behavior.drag()
.origin(function() {
var t = d3.select(this);
return {
x: t.attr("x") + d3.transform(t.attr("transform")).translate[0],
y: t.attr("y") + d3.transform(t.attr("transform")).translate[1]
};
})
.on('dragstart', function() {
d3.event.sourceEvent.stopPropagation();
})
.on('drag', function() {
var g = d3.select(this);
var parent = d3.select('#a').select('rect');
var current = d3.select(this).select('rect');
var dx = d3.event.x;
var dy = d3.event.y;
var x, y;
if (((Number(current.attr('x')) + dx) > Number(parent.attr('width')))) {
console.log('x case');
x = Number(parent.attr('width')) - Number(current.attr('width')) - Number(current.attr('x'));
} else if (dx < 0 && (Number(current.attr('x')) - Number(parent.attr('x')) + dx) < 0) {
x = -1 * Number(current.attr('x')) + 20;
} else {
x = dx;
}
if (((Number(current.attr('y')) + dy) > Number(parent.attr('height')))) {
y = Number(parent.attr('height')) - Number(current.attr('height')) - Number(current.attr('y'));
} else if (dy < 0 && (Number(current.attr('y')) - Number(parent.attr('y')) + dy) < 0) {
y = -1 * Number(current.attr('y')) + 20;
} else {
y = dy;
}
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')');
d3.selectAll(".line")[0]
.forEach(function(el) {
var d = d3.select(el).data()[0],
line = d3.select(el);
if (d.src == g.attr("id")) {
var pt = svg.createSVGPoint();
pt.x = parseInt(g.select("circle." + d.sPos).attr("cx"));
pt.y = parseInt(g.select("circle." + d.sPos).attr("cy"));
pt = pt.matrixTransform(g.node().getCTM());
pt = pt.matrixTransform(d3.select("#a").node().getCTM().inverse());
line.attr('x1', pt.x).attr('y1', pt.y);
} else if (d.tgt == g.attr("id")) {
var pt = svg.createSVGPoint();
pt.x = parseInt(g.select("circle." + d.ePos).attr("cx"));
pt.y = parseInt(g.select("circle." + d.ePos).attr("cy"));
pt = pt.matrixTransform(g.node().getCTM());
pt = pt.matrixTransform(d3.select("#a").node().getCTM().inverse());
line.attr('x2', pt.x)
.attr('y2', pt.y);
}
});
}))
.on('dragend', function() {
d3.select(this).call(d3.behavior.drag().origin(function() {
var t = d3.select(this);
return {
x: t.attr("x"),
y: t.attr("y")
};
}))
})
;
var dragging = false;
var drag = d3.behavior.drag()
.on("dragstart", function() {
dragging = true;
d3.event.sourceEvent.stopPropagation();
var x = d3.mouse(d3.select('#a').node())[0],
y = d3.mouse(d3.select('#a').node())[1];
d3.select('#dummyPath').attr('x1', x).attr('y1', y);
})
.on("dragend", function() {
var sCircle = d3.select(this);
var eCircle = d3.select(d3.event.sourceEvent.target);
dragging = false;
if (d3.event.sourceEvent.target.tagName == "circle" && this != d3.event.sourceEvent.target) {
var rad = 5;
var source = d3.select(this.parentNode).attr("id");
var target = d3.select(d3.event.sourceEvent.target.parentNode).attr("id");
var x1 = d3.select('#dummyPath').attr('x1'),
y1 = d3.select('#dummyPath').attr('y1'),
x2 = d3.mouse(d3.select('#a').node())[0],
y2 = d3.mouse(d3.select('#a').node())[1];
d3.select("#a")
.append("line")
.datum({
src: source,
tgt: target,
sPos: sCircle.attr("class"),
ePos: eCircle.attr("class")
})
.attr("class", "line")
.attr("x1", x1)
.attr("y1", y1)
.attr("x2", x2)
.attr('y2', y2);
}
d3.select('#dummyPath').style("display", "none");
});
d3.selectAll("circle").call(drag);
#dummyPath {
stroke: green;
stroke-width: 3px;
display:none;
stroke-dasharray: 5 7;
}
.line {
stroke: blue;
stroke-width: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="500" height="500">
<g id="a" transform="translate(0,0)">
<g id="d">
<rect x="10" y="10" width="200" height="200" fill="red"></rect>
<circle r="5" class="left" cx="10" cy="105" fill="blue"></circle>
<circle r="5" class="right" cx="210" cy="105" fill="blue"></circle>
</g>
<g id="b" class="e">
<rect x="20" y="20" width="50" height="50" fill="black"></rect>
<circle class="left" r="5" cx="20" cy="45" fill="blue"></circle>
<circle class="right" r="5" cx="70" cy="45" fill="blue"></circle>
</g>
<g id="c" class="e">
<rect x="90" y="20" width="50" height="50" fill="black"></rect>
<circle r="5" class="left" cx="90" cy="45" fill="blue"></circle>
<circle r="5" class="right" cx="140" cy="45" fill="blue"></circle>
</g>
<line id="dummyPath" x1="0" y1="0" x2="0" y2="0" class="dummy"></line>
</g>
</svg>
更新:
JSFiddle
相关文章:
- 在地图上画一条路线
- 在d3中拖动一条线
- 我确实有一个表单,如果用户输入了输入,它应该检查否定或空的输入框,并抛出一条警告消息
- 我正在创建一个聊天,但每次我发送消息时,它都不会让我再发送另一条消息,就像表格一样;不起作用
- 当我的条件为true时,显示一条弹出消息
- 如何在javascript中的if或else块中运行一条jsp语句
- 加载页面每10秒显示一条不同的消息
- 更改路线时,在react.js中得到一条非常crypric的消息
- 当用户单击保存按钮时,标签会显示一条消息
- 仅显示一条通用消息,而不是每个输入显示一条
- 在检索到最后一条记录后从 PostgreSQL 中提取记录
- 有没有一条清晰的路径,我可以实现一个在 ScalaJS + React 中广泛使用上下文的 React 组件
- 如果用户的浏览器早于以下版本,则显示一条消息:IE 10、Firefox 39、Chrome 39、Opera 8
- 子网格:限制用户仅选择一条记录
- 删除高图中的第一条和最后一条网格线
- 我的代码给了我一条错误消息:GetElementById()为null或不是对象
- Snap.如何始终用一条线连接两个对象
- D3用一条线连接来自不同组的矩形
- 如何在Google maps api3中用一条线连接两个标记
- 绘制一条连接数据点的线