如何使d3路径笔划内部曲线?就像“基数闭合”插值相反

时间:2015-11-13 22:09:28

标签: javascript d3.js graph curve

我正在开发一个新项目,d3创建一个显示从0到10的分数的图表。数据如下所示:

var data = [
    {axis: 'People', value: getRandomScore()}, 
    {axis: 'Leadership', value: getRandomScore()}, 
    {axis: 'Resources', value: getRandomScore()},
    {axis: 'Processes', value: getRandomScore()},
    {axis: 'Strategy', value: getRandomScore()},
    {axis: 'Culture', value: getRandomScore()},
];

getRandomScore是一个从0到10返回随机int的方法。 这是我成功实现的结果: Current result

这是艺术品: Artwork graph

问题是d3没有选项让路径描边(通过点)在内部弯曲,使路径看起来像星形图像中的星形,我需要一些帮助创建一个将要执行的功能内部曲线的东西。

1 个答案:

答案 0 :(得分:2)

你没有指明你的绘画方式,所以在我的回答中我会假设它是使用d3.svg.line.radial的极坐标图的变体。获得您所看到的外观的最简单方法是在现有的点之间插入“假”点以强制插入内部。

说你的点位于圆形极点上(x是弧度):

var data = [
  [Math.PI / 3, someValue],
  [0 * Math.PI, someValue],
  [(5 * Math.PI / 3), someValue],
  [(4 * Math.PI / 3), someValue],
  [Math.PI, someValue],
  [(2 * Math.PI) / 3, someValue]
];

然后使用那些弧度的中点:

var midPoints = [Math.PI / 6, (11 * Math.PI) / 6, 
  (3 * Math.PI) / 2, (7 * Math.PI) / 6, 
  (5 * Math.PI) / 6, Math.PI / 2];    
pD = [];
midPoints.forEach(function(d,i){
  var i2 = (i === 5) ? 0 : i + 1;
  var midY = d3.min([data[i][1],data[i2][1]]) / 2; // find the min of the two neighboring points and the "inner" fake to half the min
  pD.push(data[i]);
  pD.push([d, midY]);
});

我将内心假点设置为两个相邻点的最小值的一半。你可以调整它以获得你想要的效果。

这是完整的工作代码:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .frame {
    fill: none;
    stroke: #000;
  }
  
  .axis text {
    font: 10px sans-serif;
  }
  
  .axis line,
  .axis circle {
    fill: none;
    stroke: steelblue;
    stroke-dasharray: 4;
  }
  
  .axis:last-of-type circle {
    stroke: steelblue;
    stroke-dasharray: none;
  }
  
  .line {
    fill: none;
    stroke: orange;
    stroke-width: 3px;
  }
</style>

<body>
  <script src="//d3js.org/d3.v3.min.js"></script>
  <script>
    var width = 500,
      height = 500,
      radius = Math.min(width, height) / 2 - 30;

    var r = d3.scale.linear()
      .domain([0, 2])
      .range([0, radius]);

    var line = d3.svg.line.radial()
      .radius(function(d) {
        return r(d[1]);
      })
      .angle(function(d) {
        return -d[0] + Math.PI / 2;
      });

    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var gr = svg.append("g")
      .attr("class", "r axis")
      .selectAll("g")
      .data(r.ticks(3).slice(1))
      .enter().append("g");

    gr.append("circle")
      .attr("r", r);

    var ga = svg.append("g")
      .attr("class", "a axis")
      .selectAll("g")
      .data(d3.range(0, 360, 60))
      .enter().append("g")
      .attr("transform", function(d) {
        return "rotate(" + -d + ")";
      });

    ga.append("line")
      .attr("x2", radius);

    var line = d3.svg.line.radial()
      .radius(function(d) {
        return r(d[1]);
      })
      .angle(function(d) {
        return -d[0] + Math.PI / 2;
      })
      .interpolate("cardinal-closed");

    var data = [
      [Math.PI / 3, Math.random() + 1],
      [0 * Math.PI, Math.random() + 1],
      [(5 * Math.PI / 3), Math.random() + 1],
      [(4 * Math.PI / 3), Math.random() + 1],
      [Math.PI, Math.random() + 1],
      [(2 * Math.PI) / 3, Math.random() + 1]
    ]

    var midPoints = [Math.PI / 6, (11 * Math.PI) / 6, 
      (3 * Math.PI) / 2, (7 * Math.PI) / 6, 
      (5 * Math.PI) / 6, Math.PI / 2];
    
    pD = [];
    midPoints.forEach(function(d,i){
      var i2 = (i === 5) ? 0 : i + 1;
      var midY = d3.min([data[i][1],data[i2][1]]) / 2;
      pD.push(data[i]);
      pD.push([d, midY]);
    });

    svg.selectAll("point")
      .data(data)
      .enter()
      .append("circle")
      .attr("class", "point")
      .attr("transform", function(d) {
        var coors = line([d]).slice(1).slice(0, -1);
        return "translate(" + coors + ")"
      })
      .attr("r", 8)
      .attr("fill", "steelblue");

    svg.append("path")
      .datum(pD)
      .attr("class", "line")
      .attr("d", line);
  </script>