从原点到圆的外半径绘制一条路径

时间:2018-07-23 10:32:10

标签: javascript d3.js svg annotations

我正在向图表添加注释,并绘制从标签到其所标记的圆心的弯曲路径。从确定的原点(源)到目标点(圆心)绘制路径。我通过返回路径字符串来做到这一点。出于审美原因,我不想使用弧线作为控制线的曲线的重要依据,它取决于线源相对于圆的位置。

我使用以下命令返回路径字符串。这给了我图A

newX = sourceX
newY = sourceY
c1x = newX + ((targetX-newX) * 0.8)
c1y = newY - rem
c2x = targetX - ((targetX-newX)* 0.05)
c2y = targetY - ((targetY-newY) * 0.8)
pathString  = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + " + c2x + "," + c2y + " " + targetX + "," + targetY;};
return pathString

我已经尝试过寻找这一点,但似乎所有直线都是直线,我可以理解。但是我不知道如何获得图B,因为我掌握了图中的信息。任何帮助表示赞赏。

enter image description here

1 个答案:

答案 0 :(得分:2)

最常用的数学解决方案,例如this one,可能不适合您,因为您希望弯曲路径以虚构的方式一直延续到目标圆心。

虽然您可以为此创建更精细的数学运算(或使用comments中的建议库),但一种非常简单的解决方案是不作任何更改即可绘制路径,然后使用stroke-dasharray,删除最后一部分,使路径恰好在圆的边界处结束(几乎,请参见下面的脚本)。

因此,假设使用此路径(在这里,我正在使用您的函数,我将其命名为drawPath):

var svg = d3.select("svg");
var sourceX = 50,
  sourceY = 50;
var targetX = 300,
  targetY = 150;
var radius = 50;
svg.append("circle")
  .attr("cx", sourceX)
  .attr("cy", sourceY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", radius)
  .style("fill", "none")
  .style("stroke", "black");
svg.append("path")
  .style("fill", "none")
  .style("stroke", "steelblue")
  .style("stroke-width", "2px")
  .attr("d", drawPath);

function drawPath() {
  newX = sourceX
  newY = sourceY
  c1x = newX + ((targetX - newX) * 0.5)
  c1y = newY - ((targetY - newY) * 0.5)
  c2x = targetX - ((targetX - newX) * 0.05)
  c2y = targetY - ((targetY - newY) * 0.5)
  pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
  return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>

我们可以将stroke-dasharray更改为删除路径的最后一部分,

.attr("stroke-dasharray", function() {
    return this.getTotalLength() - radius;
});

这是结果代码:

var svg = d3.select("svg");
var sourceX = 50,
  sourceY = 50;
var targetX = 300,
  targetY = 150;
var radius = 50;
svg.append("circle")
  .attr("cx", sourceX)
  .attr("cy", sourceY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", radius)
  .style("fill", "none")
  .style("stroke", "black");
svg.append("path")
  .style("fill", "none")
  .style("stroke", "steelblue")
  .style("stroke-width", "2px")
  .attr("d", drawPath)
  .attr("stroke-dasharray", function() {
    return this.getTotalLength() - radius;
  });
svg.append("circle")
  .attr("cx", function(d) {
    var path = d3.select("path").node()
    var point = path.getPointAtLength(path.getTotalLength() - radius);
    return point.x
  })
  .attr("cy", function(d) {
    var path = d3.select("path").node()
    var point = path.getPointAtLength(path.getTotalLength() - radius);
    return point.y
  })
  .attr("r", 4);

function drawPath() {
  newX = sourceX
  newY = sourceY
  c1x = newX + ((targetX - newX) * 0.5)
  c1y = newY - ((targetY - newY) * 0.5)
  c2x = targetX - ((targetX - newX) * 0.05)
  c2y = targetY - ((targetY - newY) * 0.5)
  pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
  return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>

如果我们添加另一条未修改的红色路径,您会看到路径将一直延续到圆心:

var svg = d3.select("svg");
var sourceX = 50,
  sourceY = 50;
var targetX = 300,
  targetY = 150;
var radius = 50;
svg.append("circle")
  .attr("cx", sourceX)
  .attr("cy", sourceY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", 4);
svg.append("circle")
  .attr("cx", targetX)
  .attr("cy", targetY)
  .attr("r", radius)
  .style("fill", "none")
  .style("stroke", "black");
svg.append("path")
  .style("fill", "none")
  .style("stroke", "tomato")
  .style("stroke-width", "2px")
  .style("stroke-dasharray", "2,2")
  .attr("d", drawPath);
svg.append("path")
  .style("fill", "none")
  .style("stroke", "steelblue")
  .style("stroke-width", "2px")
  .attr("d", drawPath)
  .attr("stroke-dasharray", function() {
    return this.getTotalLength() - radius;
  });
svg.append("circle")
  .attr("cx", function(d) {
    var path = d3.select("path").node()
    var point = path.getPointAtLength(path.getTotalLength() - radius);
    return point.x
  })
  .attr("cy", function(d) {
    var path = d3.select("path").node()
    var point = path.getPointAtLength(path.getTotalLength() - radius);
    return point.y
  })
  .attr("r", 4);

function drawPath() {
  newX = sourceX
  newY = sourceY
  c1x = newX + ((targetX - newX) * 0.5)
  c1y = newY - ((targetY - newY) * 0.5)
  c2x = targetX - ((targetX - newX) * 0.05)
  c2y = targetY - ((targetY - newY) * 0.5)
  pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
  return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>

PS:值得一提的是,我在这里(错误地)假设该路径在圆内的部分的长度等于圆的半径。事实并非如此:如果两点距离不太近,则图表上的差异在视觉上可以忽略不计,但是在数学上定义得很好。但是,如果原点靠近圆的边界,则差异会很明显。在这种情况下,请使用指示的库。