D3:螺旋图

时间:2016-11-10 17:34:19

标签: javascript d3.js

有人可以帮助我使用d3.js实现类似于此的螺旋图吗? https://en.wikipedia.org/wiki/File:Condegram_Spiral_Plot.png

我刚刚获得了基本的螺旋图(一个简单的图),但是根据图像中所示的时间轴无法将图形附加到图上。我正在尝试一些事情(如果你看到评论的代码)。

https://jsfiddle.net/6bhdqqrf/1/

      var width = 400,
      height = 430,
      axes = 12,
      tick_axis = 9,
      start = 0,
      end = 2.25;

  var theta = function(r) {
      return 2 * Math.PI * r;
  };

  var angle = d3.scale.linear()
      .domain([0, axes]).range([0, 360])

  var r = d3.min([width, height]) / 2 - 40;
  var r2 = r;

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

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

  var points = d3.range(start, end + 0.001, (end - start) / 1000);

  var spiral = d3.svg.line.radial()
      .interpolate("cardinal")
      .angle(theta)
      .radius(radius);

  var path = svg.selectAll(".spiral")
      .data([points])
      .enter().append("path")
      .attr("class", "spiral")
      .attr("d", spiral)


  var z = d3.scale.category20();

  var circles = svg.selectAll('.circle')
      .data(points);

  /*  circles.enter().append('circle')
              .attr('r', 5)
          .attr('transform', function(d) { return 'translate(' + d + ')'})
          .style('fill', function(d) { return z(d); });

      */

  var circle = svg.append("circle")
      .attr("r", 13)
      .attr("transform", "translate(" + points[0] + ")");

  var movingCircle = circle.transition().duration(4000)
      .attrTween('transform', translateAlongPath(path.node()))
      //            .attr('cx', function(d) { return radius(d) * Math.cos(theta(d))})
      //        .attr('cy', function(d) { return radius(d) * Math.sin(theta(d))})


  function translateAlongPath(path) {
      var l = path.getTotalLength();
      return function(d, i, a) {
          return function(t) {

              var p = path.getPointAtLength(t * l);
              //console.log(p)
              return "translate(" + p.x + "," + p.y + ")";
          };
      };
  }

  function pathXY(path) {
      var l = path.getTotalLength();
      var start = 0;

      /*  for(i=start; i<l; i++) {
          var point = path.getPointAtLength(i);
          svg.append('rect').transition().duration(400).attr('transform', 'translate(' + point.x +','+point.y+')')
          .attr('width', 10).attr('height', 30).style('fill', z);
        }*/
  }
  pathXY(path.node());

  /*var test = translateAlongPath(path.node())()();
  //console.log(test)
  var bars = svg.selectAll('.bar')
      .data(points).enter().append('rect').transition().duration(2000)
  //  .attrTween('transform', translateAlongPath(path.node()))
    .attr('class', 'bar')
    .attr('width', 10)
    .attr('height', 20)
    .style('fill', function(d) { return z(d)});
  */
  var rect = svg.append('rect').attr('width', 10).attr('height', 10);
  rect.transition().duration(3400)
      .attrTween('transform', translateAlongPath(path.node()));

有一些类似的例子(即螺旋时间线图)很棒。

感谢。

1 个答案:

答案 0 :(得分:3)

很高兴你回来并更新了你的问题,因为这是一个有趣的问题。这是一个运行最小的实现。我已经评论好了,如果您有任何问题,请告诉我......

&#13;
&#13;
<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.js"></script>
</head>

<body>
  <div id="chart"></div>
  <script>
    var width = 500,
      height = 500,
      start = 0,
      end = 2.25,
      numSpirals = 4;

    var theta = function(r) {
      return numSpirals * Math.PI * r;
    };

    var r = d3.min([width, height]) / 2 - 40;

    var radius = d3.scaleLinear()
      .domain([start, end])
      .range([40, r]);

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

    // create the spiral, borrowed from http://bl.ocks.org/syntagmatic/3543186
    var points = d3.range(start, end + 0.001, (end - start) / 1000);

    var spiral = d3.radialLine()
      .curve(d3.curveCardinal)
      .angle(theta)
      .radius(radius);

    var path = svg.append("path")
      .datum(points)
      .attr("id", "spiral")
      .attr("d", spiral)
      .style("fill", "none")
      .style("stroke", "steelblue");

    // fudge some data, 2 years of data starting today
    var spiralLength = path.node().getTotalLength(),
        N = 730,
        barWidth = (spiralLength / N) - 1;
    var someData = [];
    for (var i = 0; i < N; i++) {
      var currentDate = new Date();
      currentDate.setDate(currentDate.getDate() + i);
      someData.push({
        date: currentDate,
        value: Math.random()
      });
    }
    
    // here's our time scale that'll run along the spiral
    var timeScale = d3.scaleTime()
      .domain(d3.extent(someData, function(d){
        return d.date;
      }))
      .range([0, spiralLength]);
    
    // yScale for the bar height
    var yScale = d3.scaleLinear()
      .domain([0, d3.max(someData, function(d){
        return d.value;
      })])
      .range([0, (r / numSpirals) - 30]);

    // append our rects
    svg.selectAll("rect")
      .data(someData)
      .enter()
      .append("rect")
      .attr("x", function(d,i){
        
        // placement calculations
        var linePer = timeScale(d.date),
            posOnLine = path.node().getPointAtLength(linePer),
            angleOnLine = path.node().getPointAtLength(linePer - barWidth);
      
        d.linePer = linePer; // % distance are on the spiral
        d.x = posOnLine.x; // x postion on the spiral
        d.y = posOnLine.y; // y position on the spiral
        
        d.a = (Math.atan2(angleOnLine.y, angleOnLine.x) * 180 / Math.PI) - 90; //angle at the spiral position

        return d.x;
      })
      .attr("y", function(d){
        return d.y;
      })
      .attr("width", function(d){
        return barWidth;
      })
      .attr("height", function(d){
        return yScale(d.value);
      })
      .style("fill", "steelblue")
      .style("stroke", "none")
      .attr("transform", function(d){
        return "rotate(" + d.a + "," + d.x  + "," + d.y + ")"; // rotate the bar
      });
    
    // add date labels
    var tF = d3.timeFormat("%b %Y"),
        firstInMonth = {};
    svg.selectAll("text")
      .data(someData)
      .enter()
      .append("text")
      .attr("dy", 10)
      .style("text-anchor", "start")
      .style("font", "10px arial")
      .append("textPath")
      // only add for the first of each month
      .filter(function(d){
        var sd = tF(d.date);
        if (!firstInMonth[sd]){
          firstInMonth[sd] = 1;
          return true;
        }
        return false;
      })
      .text(function(d){
        return tF(d.date);
      })
      // place text along spiral
      .attr("xlink:href", "#spiral")
      .style("fill", "grey")
      .attr("startOffset", function(d){
        return ((d.linePer / spiralLength) * 100) + "%";
      })

  </script>
</body>

</html>
&#13;
&#13;
&#13;