1 回答

TA貢獻1900條經驗 獲得超5個贊
考慮以下迭代方法:
使用path.getPointAtLength
,您可以遍歷路徑,直到找到恰好r
位于圓心的點,然后使用這些坐標重新繪制路徑。
const data = [{
? x: 50,
? y: 100,
? r: 20
}, {
? x: 100,
? y: 30,
? r: 5
}];
const links = [{
? ? source: data[0],
? ? target: data[1]
? },
? {
? ? source: data[1],
? ? target: data[0]
? }
];
positionLink = (source, target) => {
? const offsetPx = 100;
? const midpoint = {
? ? x: (source.x + target.x) / 2,
? ? y: (source.y + target.y) / 2
? };
? const dx = source.x - target.x;
? const dy = source.y - target.y;
? // Perpendicular vector?
? const nx = -dy;
? const ny = dx;
? const norm_length = Math.sqrt((nx * nx) + (ny * ny));
? const normx = nx / norm_length;
? const normy = ny / norm_length;
? const offset = {
? ? x: parseFloat(midpoint.x + offsetPx * normx.toFixed(2)),
? ? y: parseFloat(midpoint.y + offsetPx * normy.toFixed(2)),
? };
? const arc = `M ${source.x.toFixed(2)} ${source.y.toFixed(2)} S ${offset.x} ${offset.y} ${target.x.toFixed(2)} ${target.y.toFixed(2)}`;
? return arc;
};
euclidean = (point, other) => Math.sqrt(point.x * other.x + point.y * other.y);
findPointAtLength = (path, point, fromEnd) => {
? // For the target we need to start at the other side of the path
? let offset = point.r;
? if (fromEnd) {
? ? const totalLength = path.getTotalLength();
? ? offset = totalLength - offset;
? }
? let current = path.getPointAtLength(offset);
? // Gradually increase the offset until we're exactly?
? // `r` away from the circle centre
? while (euclidean(point, current) < point.r) {
? ? offset += 1;
? ? current = path.getPointAtLength(offset);
? }
? return {
? ? x: current.x,
? ? y: current.y
? };
};
// Use function because we want access to `this`,
// which points to the current path HTMLElement
positionLinkAtEdges = function(d) {
? // First, place the path in the old way
? d3.select(this).attr("d", positionLink(d.source, d.target));
? // Then, position the path away from the source
? const source = findPointAtLength(this, d.source, false);
? const target = findPointAtLength(this, d.target, true);
? return positionLink(source, target);
}
const svg = d3.select("svg").append("g");
svg
? .selectAll("circle")
? .data(data)
? .enter()
? .append("circle")
? .attr("cx", d => d.x)
? .attr("cy", d => d.y)
? .attr("r", d => d.r);
svg
? .selectAll("path")
? .data(links)
? .enter()
? .append("path")
? .attr("d", positionLinkAtEdges)
? .attr("marker-end", "url(#triangle)");
g circle,
g path {
? fill: none;
? stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg>
? <defs>
? ? <marker id="triangle" viewBox="0 0 10 10"
? ? ? ? ? refX="10" refY="5"?
? ? ? ? ? markerUnits="strokeWidth"
? ? ? ? ? markerWidth="10" markerHeight="10"
? ? ? ? ? orient="auto">
? ? ? <path d="M 0 0 L 10 5 L 0 10 z" fill="#f00"/>
? ? </marker>
? </defs>
</svg>
添加回答
舉報