D3.js如何使用转换函数更新节点大小时运行forceSimulation

时间:2018-06-15 05:41:20

标签: javascript d3.js

我有一堆代表主题的行,以及代表年份的列,每个单元格中的值是该年度主题的相关性分数。每个主题都是d3图中的一个节点,我希望将滑块作为HTML元素,以便用户可以输入他们想要查看其值的年份,并且节点会更改大小来表示这一点。我正在使用转换函数使节点大小更改看起来很平滑。

我最初有一个forceSimulation函数,它使用chargecollision来确保没有任何节点在它们的起始位置重叠。但是,当节点的大小增加时,它们往往会重叠。我如何组织代码,以便在更新尺寸时再次运行forceSimulation,同时保持过渡平稳?

<!DOCTYPE html>

<div class="chart-example" id="chart"><svg></svg></div>

<p>
  <label for="year"
         style="display: inline-block; width: 240px; text-align: right">
         year = <span id="year-value">…</span>
  </label>
  <input type="range" min="2000" max="2001" id="year">
</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.min.js"></script>

<script>

d3.csv("{{url_for('static', filename = 'data.csv')}}", function(error, data) {
  if (error) {
      console.error('Error getting or parsing the data.');
      throw error;
  }
  // selection.datum() returns the bound datum for the first element in the selection and
  //  doesn't join the specified array of data with the selected elements
  var chart = bubbleChart().width(600).height(400);
  d3.select('#chart').datum(data).call(chart);
});

function bubbleChart() {
  // use the year to get the column/index of data file that contains the radius data for the nodes
  function getRadiusCol(year) {
    var year_index = year - 1999;
    var index = year_index.toString();
    return index;
  }

  var width = 960,
      height = 960,
      maxRadius = 6,
      columnForColors = "category",
      // Initialize this to data from the year 2000 -> should this just be a variable since we
      // set update(2000) a few lines above to intialize this?
      columnForRadius = "1";

  function chart(selection) {
      // when the slider value changes call the update function to change node sizes
      d3.select("#year").on("input", function() {
        update(+this.value);
      });
      var data = selection.datum();
      var div = selection,
          svg = div.selectAll('svg');
      svg.attr('width', width).attr('height', height);

      var tooltip = selection
          .append("div")
          .style("position", "absolute")
          .style("visibility", "hidden")
          .style("color", "white")
          .style("padding", "8px")
          .style("background-color", "#626D71")
          .style("border-radius", "6px")
          .style("text-align", "center")
          .style("font-family", "monospace")
          .style("width", "400px")
          .text("");

      var simulation = d3.forceSimulation(data)
          .force("charge", d3.forceManyBody().strength([-50]))
          .force("collision", d3.forceCollide().radius(function(d) {
    return d.radius}))
          .force("x", d3.forceX())
          .force("y", d3.forceY())
          .on("tick", ticked);
          //.on("")

      function ticked(e) {
          node.attr("cx", function(d) {
                  return d.x;
              })
              .attr("cy", function(d) {
                  return d.y;
              });
      }

      var colorCircles = d3.scaleOrdinal(d3.schemeCategory10);
      var scaleRadius = d3.scaleLinear().domain([d3.min(data, function(d) {
          return +d[columnForRadius];
      }), d3.max(data, function(d) {
          return +d[columnForRadius];
      })]).range([5, 18])

      var node = svg.selectAll("circle")
          .data(data)
          .enter()
          .append("circle")
          // This is the line that sets the radius -> how can we use transition to redo the transition
          .attr('r', function(d) {
              return scaleRadius(d[columnForRadius])
          })
          .style("fill", function(d) {
              return colorCircles(d[columnForColors])
          })
          .attr('transform', 'translate(' + [width / 2, height / 2] + ')')
          .on("mouseover", function(d) {
              tooltip.html(d[columnForColors] + "<br>" + d.title + "<br>" + d[columnForRadius] + " imapct");
              return tooltip.style("visibility", "visible");
          })
          .on("mousemove", function() {
              return tooltip.style("top", (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px");
          })
          .on("mouseout", function() {
              return tooltip.style("visibility", "hidden");
          });

    // Initial the nodes with data from the year 2000
      update(2000);

      // update things based on what year the slider is set to
      function update(year) {
        console.log("in update");
        // adjust the text on the range slider
        d3.select("#year-value").text(year);
        d3.select("#year").property("value", year);
        // update the circle radius
        var columnForRadius = getRadiusCol(year);
        node.data(data)
          .transition()
          .duration(1000)
          .attr('r', function(d) {
              return scaleRadius(d[columnForRadius])
          })
    }
  }

  chart.columnForRadius = function(value) {
      if (!arguments.columnForRadius) {
          return columnForRadius;
      }
      columnForRadius = value;
      return chart;
  };

  return chart;
}
</script>

0 个答案:

没有答案