【问题标题】:Draw a path from an origin point to the outside radius of a circle绘制从原点到圆外半径的路径
【发布时间】:2018-07-23 10:32:10
【问题描述】:

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

我正在使用以下内容返回路径字符串。这给了我图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。任何帮助表示赞赏。

【问题讨论】:

  • 看看svg-intersections 库,它在计算 SVG 形状的交点时会起到很大的作用。您可能还想看看它的前身,Kevin Lindsey 的 Intersection 库,我在许多情况下更喜欢它,因为它易于使用。无论如何,结果都是一样的。

标签: javascript d3.js svg annotations


【解决方案1】:

最常用的数学解决方案,例如 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:值得一提的是,在这里我假设(错误地)圆内该路径部分的长度等于圆的半径。不是:如果两个点不是很接近,则图表的差异在视觉上可以忽略不计,但它在数学上定义明确。但是,如果原点靠近圆的边界,则差异将很明显。在这种情况下,请使用指定的库。

【讨论】:

  • 感谢 Gerardo。我喜欢 'stroke-dasharray' 的简单性,但是我的标签是可拖动的,因此当我的标签移动时,路径会改变比例,从而显示破折号。最常见的解决方案几乎就是我所追求的,但这又使用了并不总是能提供良好视觉外观的弧线。也就是说,如果我计算逆时针弧和顺时针弧并使用可能有效的适当弧。谢谢
  • 但是,如果我在重绘时将“stroke-dasharray”添加到拖动函数中的路径,它会很有效。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多