【问题标题】:D3 small circle located on edge of radius of larger circleD3 小圆位于大圆半径的边缘
【发布时间】:2017-06-02 17:06:58
【问题描述】:

我正在制作带有两个圆圈的 d3 视觉效果,其中较小的圆圈位于较大圆圈的半径边缘。这个代码笔位于https://codepen.io/ajblack/pen/KqKpde。我有一个 200 像素的方形 SVG,我将这些圆圈放入其中,然后我有两个值,uAssets 和 uAssetsCom,它们指示圆圈的大小。我使用以下代码创建了一个比率以确保较大的圆圈适合 200 像素的正方形:

var ratioToFit = (200/uAssets)/2;

然后我用余弦计算大圆的外半径与SVG容器外边界45度角的距离:

var key = 100*((1-Math.cos(45*Math.PI/180))/Math.cos(45*Math.PI/180))

为了放置较小的圆,我将cxcy 属性计算为内圆的半径加上这个“键”。

d3.select("#uAssetComCircle")
  .attr("cx", function(d){return (d.radius+key);})
  .attr("cy", function(d){return (d.radius+key);})

这会使内圆靠近大圆的半径,但距离大约 2 个像素。我可以通过在cxcy 之外添加硬编码的 2 像素来抵消这一点,但我觉得我不应该这样做。

我的数学有问题吗?

【问题讨论】:

  • 我相信错误出在“关键”计算中。我没有准确追踪它,但尝试将角度从 45 度更改为 60 度,您会发现它并没有达到您想要的效果

标签: javascript d3.js svg


【解决方案1】:

不确定我是否完全按照您的计算,但这里有一个快速重构,它引入了一些东西:

  1. 将所有绘图从 SVG 的中心点转换为基础
  2. 一种 d3 比例尺,用于处理从用户坐标到像素坐标的计算。

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body>
  <script>
    var uAssets = 17;
    var uAssetsCom = 3;

    var svgContainer = d3.select("body").append("svg")
      .attr("width", 200)
      .attr("height", 200)
      .attr('id', 'circleContainer')
      .append("g")
      .attr("transform", "translate(" + 100 + "," + 100 + ")")

    var scale = d3.scaleLinear()
      .domain([0,uAssets])
      .range([0,100]);

    var jsonCircles = [{
      "radius": uAssets,
      "color": "green",
      "id": "uAssetCircle"
    }, {
      "radius": uAssetsCom,
      "color": "purple",
      "id": "uAssetComCircle"
    }];

    var circles = svgContainer.selectAll("circle")
      .data(jsonCircles)
      .enter()
      .append("circle");

    var circleAttributes = circles
      .attr("r", function(d) {
        return scale(d.radius);
      })
      .attr("id", function(d) {
        return d.id
      })
      .style("fill", function(d) {
        return d.color;
      });

    d3.select("#uAssetComCircle")
      .attr("cx", function(d) {
        return scale(uAssets) * Math.cos(45 * (Math.PI / 180));
      })
      .attr("cy", function(d) {
        return scale(uAssets) * Math.sin(45 * (Math.PI / 180));
      })
  </script>
</body>

</html>

【讨论】:

  • 为什么建议使用翻译来使圆圈居中而不是使用cxcyproperties 来使圆圈居中?我一直在使用这个线程link 中描述的方法来计算 200 像素边界框的角和 100 像素半径圆之间的距离。该度量值就是我的代码中的关键值所代表的。
  • 我能够通过创建一个斜边为(较大半径 - 较小半径)的三角形并求解两个相等长度的边来解决我的问题。我的cxcy 测量值就是这样计算的。
  • @ablackman93,我将整个绘图区域转换为 SVG 的中心,因为我使用的三角函数在中心有一个原点。我很高兴你明白了。你应该有足够的声望来自我回答你自己的问题;请这样做,并记得在 48 小时后回来接受它。我很想读。
【解决方案2】:

我的解决方案发布在这里 https://codepen.io/ajblack/pen/qjOqmQ 。 此实现使用缩放和变换的建议。我遇到的问题是对勾股定理的误用。我的大圆的半径是 100px,我可以用它作为直角三角形的斜边。我知道我希望我的小圆以 45 度角偏移,所以我知道三角形的另外两个边的长度相同。使用公式 a²+b²=c² 其中 a 和 b 是相同的值,c 等于 100px 减去我的小圆的半径,我可以轻松求解 a 和 b。

var uAssets = 17;
var uAssetsCom = 3;
var scale = d3.scaleLinear()
.domain([0,uAssets])
.range([0,100]);
 d3.select("#uAssetComCircle")
    .attr("cx", function(d) {
    return Math.sqrt(Math.pow(100-scale(uAssetsCom),2)/2);
  })
    .attr("cy", function(d) {
    return Math.sqrt(Math.pow(100-scale(uAssetsCom),2)/2);
  });

成品长这样

【讨论】:

    最近更新 更多