目标 SVG 路径到 rect 元素的中间
Target SVG path to the middle of rect element
我正在使用 D3 库绘制元素的互连图。我的节点是 circles
和 rects
由定向线 path
s.
连接
我的问题是指向 rects
元素的线有一个丑陋的可视化,因为线结束于矩形的左上角而不是它的中心(就像它对圆圈一样)。
如何使路径线同时指向 circles
元素和 rect
元素的中心?
defs
箭头定义代码:
svg.append('defs')
.append('marker')
.attr('id', 'arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 17) // As far as I understood this provides the distance from the end of the path line.
.attr('refY', -0.1)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.attr('fill', function() {
return 'red';
})
.append('path')
.attr('d', 'M0,-5L10,0L0,5');
定向链接的定义:
let links = svg.selectAll('.link')
.data(data.links)
.enter()
.append('path')
.attr('id', function (d) {
return d.id;
})
.attr('class', 'link')
.attr('fill', 'none')
.attr('stroke-width', 1.2)
.attr('marker-end', 'url(#arrow)')
.attr('stroke', function() {
return 'blue';
})
.style('cursor', 'pointer');
正方形的定义
let squares = svg.selectAll('.square')
.data(data.squares, function(d) {
return d.id;
})
.enter().append('g')
.call(dragger)
.attr('class', 'square')
.style('cursor', 'pointer');
squares.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr('fill', function (d) {
return '#fff';
})
.style('opacity', 0.1)
.style('stroke', function() {
return '#555';
})
.style('stroke-width', '2');
在下面的屏幕截图中,您可以看到它的行为方式。圆形和矩形的不透明度较低,无法显示路径目标的问题。
更新
添加了 tick
函数定义和用法。
simulation
.nodes(data.nodes)
.on('tick', _tick);
simulation
.force('link')
.distance(80)
.links(data.links);
simulation.alpha(1).restart();
function _tick() {
links.attr('d', function(d) {
let dx = d.target.x - d.source.x;
let dy = d.target.y - d.source.y;
let dr = Math.sqrt(dx * dx + dy * dy);
return ('M' + d.source.x + ',' + d.source.y +
'A' + dr + ',' + dr + ' 0 0,1 ' + d.target.x + ',' + d.target.y);
});
circles.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
squares.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
}
- 你能翻译这个广场吗?如果你能翻译它一半的正方形
宽度。
- 你能找到像 x2,y2 这样的正方形路径的终点吗?加上你正方形宽度的一半,.
"hope this inspired you"
squares.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr("transform", "translate(-5,0)")
.attr('fill', function (d) {
return '#fff';
})
您现在拥有的是预期的行为。在力模拟中(我假设你是 运行 力模拟),tick
函数改变了基准对象的 x
和 y
属性,你可以使用随心所欲。
由于您没有分享您的 tick
功能,我想您正在像这样更新矩形的 x
和 y
位置:
squares.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
如果这实际上是正确的,则矩形的左上角对应于 d.x
和 d.y
坐标。而且,由于您使用相同的属性来绘制路径,路径将从一个左上角延伸到另一个。
这很容易展示,看看这个演示:
var width = 200;
var height = 200;
var rectSide = 40;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(100))
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2));
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("rect")
.attr("width", rectSide)
.attr("height", rectSide)
.attr("fill", function(d) {
return d.color
});
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#222")
.style("stroke-width", 2);
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
node.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
<script src="https://d3js.org/d3.v4.js"></script>
解决方法:您可以移动矩形或路径。
由于您的问题具体询问了路径,解决方案很简单:将半宽和半高添加到目标和源坐标:
link.attr("x1", function(d) {
return d.source.x + rectangleWidth / 2;
})
.attr("y1", function(d) {
return d.source.y + rectangleHeight / 2;
})
.attr("x2", function(d) {
return d.target.x + rectangleWidth / 2;
})
.attr("y2", function(d) {
return d.target.y + rectangleHeight / 2;
})
这是一个演示:
var width = 200;
var height = 200;
var rectSide = 40;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(100))
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2));
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("rect")
.attr("width", rectSide)
.attr("height", rectSide)
.attr("fill", function(d) {
return d.color
});
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#222")
.style("stroke-width", 2);
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x + rectSide / 2;
})
.attr("y1", function(d) {
return d.source.y + rectSide / 2;
})
.attr("x2", function(d) {
return d.target.x + rectSide / 2;
})
.attr("y2", function(d) {
return d.target.y + rectSide / 2;
})
node.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
<script src="https://d3js.org/d3.v4.js"></script>
我正在使用 D3 库绘制元素的互连图。我的节点是 circles
和 rects
由定向线 path
s.
我的问题是指向 rects
元素的线有一个丑陋的可视化,因为线结束于矩形的左上角而不是它的中心(就像它对圆圈一样)。
如何使路径线同时指向 circles
元素和 rect
元素的中心?
defs
箭头定义代码:
svg.append('defs')
.append('marker')
.attr('id', 'arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 17) // As far as I understood this provides the distance from the end of the path line.
.attr('refY', -0.1)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.attr('fill', function() {
return 'red';
})
.append('path')
.attr('d', 'M0,-5L10,0L0,5');
定向链接的定义:
let links = svg.selectAll('.link')
.data(data.links)
.enter()
.append('path')
.attr('id', function (d) {
return d.id;
})
.attr('class', 'link')
.attr('fill', 'none')
.attr('stroke-width', 1.2)
.attr('marker-end', 'url(#arrow)')
.attr('stroke', function() {
return 'blue';
})
.style('cursor', 'pointer');
正方形的定义
let squares = svg.selectAll('.square')
.data(data.squares, function(d) {
return d.id;
})
.enter().append('g')
.call(dragger)
.attr('class', 'square')
.style('cursor', 'pointer');
squares.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr('fill', function (d) {
return '#fff';
})
.style('opacity', 0.1)
.style('stroke', function() {
return '#555';
})
.style('stroke-width', '2');
在下面的屏幕截图中,您可以看到它的行为方式。圆形和矩形的不透明度较低,无法显示路径目标的问题。
更新
添加了 tick
函数定义和用法。
simulation
.nodes(data.nodes)
.on('tick', _tick);
simulation
.force('link')
.distance(80)
.links(data.links);
simulation.alpha(1).restart();
function _tick() {
links.attr('d', function(d) {
let dx = d.target.x - d.source.x;
let dy = d.target.y - d.source.y;
let dr = Math.sqrt(dx * dx + dy * dy);
return ('M' + d.source.x + ',' + d.source.y +
'A' + dr + ',' + dr + ' 0 0,1 ' + d.target.x + ',' + d.target.y);
});
circles.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
squares.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
}
- 你能翻译这个广场吗?如果你能翻译它一半的正方形 宽度。
- 你能找到像 x2,y2 这样的正方形路径的终点吗?加上你正方形宽度的一半,.
"hope this inspired you"
squares.append('rect')
.attr('width', 10)
.attr('height', 10)
.attr("transform", "translate(-5,0)")
.attr('fill', function (d) {
return '#fff';
})
您现在拥有的是预期的行为。在力模拟中(我假设你是 运行 力模拟),tick
函数改变了基准对象的 x
和 y
属性,你可以使用随心所欲。
由于您没有分享您的 tick
功能,我想您正在像这样更新矩形的 x
和 y
位置:
squares.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
如果这实际上是正确的,则矩形的左上角对应于 d.x
和 d.y
坐标。而且,由于您使用相同的属性来绘制路径,路径将从一个左上角延伸到另一个。
这很容易展示,看看这个演示:
var width = 200;
var height = 200;
var rectSide = 40;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(100))
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2));
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("rect")
.attr("width", rectSide)
.attr("height", rectSide)
.attr("fill", function(d) {
return d.color
});
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#222")
.style("stroke-width", 2);
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
node.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
<script src="https://d3js.org/d3.v4.js"></script>
解决方法:您可以移动矩形或路径。
由于您的问题具体询问了路径,解决方案很简单:将半宽和半高添加到目标和源坐标:
link.attr("x1", function(d) {
return d.source.x + rectangleWidth / 2;
})
.attr("y1", function(d) {
return d.source.y + rectangleHeight / 2;
})
.attr("x2", function(d) {
return d.target.x + rectangleWidth / 2;
})
.attr("y2", function(d) {
return d.target.y + rectangleHeight / 2;
})
这是一个演示:
var width = 200;
var height = 200;
var rectSide = 40;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(100))
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2));
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("rect")
.attr("width", rectSide)
.attr("height", rectSide)
.attr("fill", function(d) {
return d.color
});
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#222")
.style("stroke-width", 2);
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x + rectSide / 2;
})
.attr("y1", function(d) {
return d.source.y + rectSide / 2;
})
.attr("x2", function(d) {
return d.target.x + rectSide / 2;
})
.attr("y2", function(d) {
return d.target.y + rectSide / 2;
})
node.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
<script src="https://d3js.org/d3.v4.js"></script>