首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用d3.js绘制螺旋图上的度数

如何用d3.js绘制螺旋图上的度数
EN

Stack Overflow用户
提问于 2020-10-19 18:28:36
回答 1查看 119关注 0票数 2

我有一个正在运行的Angular 9应用程序,其中SVG有一个螺旋图,其中包含度数的最小值和最大值。我正在使用d3.js在螺旋图上绘制给定度的值。我写了以下代码:

代码语言:javascript
复制
// min -> min degree, -140 in this example
// max -> max degree, 440 in this example
// currentDegree -> degree value to be ploted, 0 in this example
// svg -> svg containing spiral chart
// circle -> circle to be moved to depict current Degree position in the svg 

void setDegree(min,max,currentDegree, svg, circle) {

    const pathNode = svg.select('path').node();
    const totalPathLength = pathNode.getTotalLength();

    const yDomain = d3.scale.linear().domain([min, max]).range(
        [0, totalPathLength]); 

    const currentPathLength = yDomain(currentDegree); // current path length
    const pathPoint =  pathNode.getPointAtLength(totalPathLength - currentPathLength);

    circle.transition()
        .duration(300)
        .attrTween('cx', () => (t) => pathPoint.x)
        .attrTween('cy', () => (t) => pathPoint.y);
}

上面的代码生成以下输出:

在上图中,0度稍微向右移动,但它应该位于中心,如下图所示:

代码语言:javascript
复制
function setDegree(min, max, currentDegree, svg, circle) {
  const pathNode = svg.select("path").node();
  const totalPathLength = pathNode.getTotalLength();

  const yDomain = d3
    .scaleLinear()
    .domain([min, max])
    .range([0, totalPathLength]);

  const currentPathLength = yDomain(currentDegree); // current path length
  const pathPoint = pathNode.getPointAtLength(
    totalPathLength - currentPathLength
  );

  circle
    .transition()
    .duration(300)
    .attrTween("cx", () => t => pathPoint.x)
    .attrTween("cy", () => t => pathPoint.y);
}

const svg = d3.select("svg");
const circle = d3.select("#cur_pos");
setDegree(-140, 410, 0, svg, circle);
代码语言:javascript
复制
p {
  font-family: Lato;
}

.cls-3 {
  fill: none;
  stroke-width: 10px;
  stroke: #000;
}

.cls-3,
.cls-4,
.cls-5 {
  stroke-miterlimit: 10;
}

.cls-4,
.cls-5 {
  stroke-width: 0.25px;
}

.cls-5 {
  font-size: 60px;
  font-family: ArialMT, Arial;
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1031.1 1010.3" preserveAspectRatio="xMinYMin meet">
  <title>Spiral Chart</title>

  <g>
    <g id="Spiral_Chart">
      <g id="Spiral_Path">
        <path
          class="cls-3 svg-range-line"
          d="M881.5,154.9C679.6-47,352.3-47,150.4,154.9c-195.9,195.9-195.9,513.4,0,709.3,189.7,190,497.5,190.1,687.5.4l.4-.4c184.3-184.3,184.3-483,0-667.3h0C659.6,18.1,369.8,18.1,191.1,196.8H191C17.6,370.3,17.6,651.4,191,824.8"
        />
      </g>
      <circle
        id="cur_pos"
        class="cls-4 svg-range-indicator"
        cx="514"
        cy="64.3"
        r="18.5"
      />
    </g>
    <text
      id="Min"
      class="cls-5 svg-text"
      style="text-anchor:start;"
      x="195"
      y="880"
    >
      -140
    </text>
    <text
      id="Max"
      class="cls-5 svg-text"
      style="text-anchor:start;"
      x="885"
      y="210"
    >
      410
    </text>
  </g>
</svg>

EN

回答 1

Stack Overflow用户

发布于 2020-10-27 22:39:31

因为你的螺旋在内部较小,如果你计算长度为0度(或90度,或-90度),你会超过它。这是因为路径的总长度包括螺旋的外部,这部分更长,因为它的半径更大。换句话说,如果路径是完全圆形的,那么您的逻辑是正确的。但这并不是说你会有一点点的偏差。

请注意,如果将currentDegree更改为360,它几乎是完美的位置。这也是因为这个半径。

我用过这个很棒的软件包kld-intersections,它可以计算两个SVG形状的交点。

我首先取圆的中点,然后在我想要圆的方向上计算一些很长的线。我计算路径与这条线的交叉点,并返回一个交叉点数组。

现在,为了知道是使用最近的交叉点还是最远的交叉点,我根据到中心的距离对它们进行排序,并检查最小角度和所需角度之间的360倍。

注意,中心点不是完美的,这就是为什么如果你将它更改为-140,圆将不会在确切的结束位置。也许你可以改进这一点,或者-如果设计是稳定的,手动计算点数。

代码语言:javascript
复制
const {
  ShapeInfo,
  Intersection
} = KldIntersections;

function getCentroid(node) {
  const bbox = node.getBBox();
  return {
    x: bbox.x + bbox.width / 2,
    y: bbox.y + bbox.height / 2,
  };
}

function setDegree(min, max, currentDegree, svg, circle) {
  const pathNode = svg.select("path").node();
  const centroid = getCentroid(pathNode);

  const pathInfo = ShapeInfo.path(pathNode.getAttribute("d"));
  // We need to draw a line from the centroid, at the angle we want the
  // circle to have.
  const currentRadian = (currentDegree / 180) * Math.PI - Math.PI / 2;
  const lineEnd = {
    // HACK: small offset so the line is never completely vertical
    x: centroid.x + 1000 * Math.cos(currentRadian) + Math.random() * 0.01,
    y: centroid.y + 1000 * Math.sin(currentRadian),
  };

  indicatorLine
    .attr("x1", centroid.x)
    .attr("y1", centroid.y)
    .attr("x2", lineEnd.x)
    .attr("y2", lineEnd.y);
  const line = ShapeInfo.line([centroid.x, centroid.y], [lineEnd.x, lineEnd.y]);
  const intersections = Intersection.intersect(pathInfo, line).points;

  // Sort the points based on their distance to the centroid
  intersections.forEach(
    p => p.dist = Math.sqrt((p.x - centroid.x) ** 2 + (p.y - centroid.y) ** 2));
  intersections.sort((a, b) => a.dist - b.dist);

  // See which intersection we need.
  // Iteratively go round the circle until we find the correct one
  let i = 0;
  while (min + 360 * (i + 1) <= currentDegree) {
    i++;
  }

  const pathPoint = intersections[i];

  circle
    .attr("cx", pathPoint.x)
    .attr("cy", pathPoint.y);
}

const svg = d3.select("svg");
const indicatorLine = svg.append("line").attr("stroke", "red");
const circle = d3.select("#cur_pos");

setDegree(-140, 410, 0, svg, circle);
d3.select("input").on("change", function() {
  setDegree(-140, 410, +this.value, svg, circle);
});
代码语言:javascript
复制
p {
  font-family: Lato;
}

.cls-3 {
  fill: none;
  stroke-width: 10px;
  stroke: #000;
}

.cls-3,
.cls-4,
.cls-5 {
  stroke-miterlimit: 10;
}

.cls-4,
.cls-5 {
  stroke-width: 0.25px;
}

.cls-5 {
  font-size: 60px;
  font-family: ArialMT, Arial;
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://unpkg.com/kld-intersections"></script>

<label>Value</label> <input type="number" value="0" min="-140" max="410"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1031.1 1010.3" preserveAspectRatio="xMinYMin meet">
  <g>
    <g id="Spiral_Chart">
      <g id="Spiral_Path">
        <path
          class="cls-3 svg-range-line"
          d="M881.5,154.9C679.6-47,352.3-47,150.4,154.9c-195.9,195.9-195.9,513.4,0,709.3,189.7,190,497.5,190.1,687.5.4l.4-.4c184.3-184.3,184.3-483,0-667.3h0C659.6,18.1,369.8,18.1,191.1,196.8H191C17.6,370.3,17.6,651.4,191,824.8"
        />
      </g>
      <circle
        id="cur_pos"
        class="cls-4 svg-range-indicator"
        cx="514"
        cy="64.3"
        r="18.5"
      />
    </g>
    <text
      id="Min"
      class="cls-5 svg-text"
      style="text-anchor:start;"
      x="195"
      y="880"
    >
      -140
    </text>
    <text
      id="Max"
      class="cls-5 svg-text"
      style="text-anchor:start;"
      x="885"
      y="210"
    >
      410
    </text>
  </g>
</svg>

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64425505

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档