使用forceSimulation更新气泡图而不更改旧气泡的位置

时间:2018-04-12 13:55:08

标签: d3.js

我希望使用D3.js v5实现一个随时间动态变化的气泡图,如this

如果您运行以下代码并移动滑动条,您会发现上一步中出现的气泡位置正在发生变化,这使得步骤之间的过渡"跳跃"。

如何尽可能保持上一步中出现的气泡位置并仅更新气泡尺寸?

换句话说,给定一组键/值对的序列,如何更新这些对而不让它们使用forceSimulation相互冲突,同时保持已经存在的键的x,y位置出现在上一个时间步骤?



var data = [
	[{text:"a", size: 500},{text:"b", size: 300},{text:"c", size: 150},{text:"d", size: 100},{text:"e", size: 200}],
  [{text:"a", size: 400},{text:"b", size: 200},{text:"c", size: 100},{text:"x", size: 300},{text:"y", size: 300}],
  [{text:"a", size: 200},{text:"b", size: 100},{text:"x", size: 300},{text:"y", size: 350},{text:"z", size: 200}],
  [{text:"a", size: 500},{text:"b", size: 300},{text:"c", size: 150},{text:"d", size: 100},{text:"e", size: 200}],
  [{text:"a", size: 400},{text:"b", size: 200},{text:"c", size: 100},{text:"x", size: 300},{text:"y", size: 300}]
  ]
  
var tooltip = d3.select("body")
  .append("div")
  .style("position", "absolute")
  .style("z-index", "10")
  .style("visibility", "hidden")
  .style("color", "white")
  .style("padding", "8px")
  .style("background-color", "rgba(0, 0, 0, 0.75)")
  .style("border-radius", "6px")
  .style("font", "14px sans-serif")
  .text("tooltip");

var width = 300,
  height = 300,
  color = d3.scaleOrdinal(d3.schemePastel1)

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

var circles = svg.selectAll(".node");
var texts = svg.selectAll(".text");

var radiusScale = d3.scaleSqrt().domain([100, 400]).range([10, 40])


var slider = d3.select("#slider")
  .append("input")
  .attr("class", "slider")
  .attr("type", "range")
  .attr("min", 0)
  .attr("max", data.length - 1)
  .attr("value", 0)
  .attr("style", "width:300px")
  .on("change", function(d) {
    update(data[this.value]);
  })

var simulation = d3.forceSimulation()
  .force("x", d3.forceX())
  .force("y", d3.forceY())
  .force("collide", d3.forceCollide(function(d) {
    return radiusScale(d.size) + 1;
  }))
  .on('tick', ticked)

// Initial update
update(data[0])

function update(data) {
  var t = d3.transition()
    .duration(750);

  //For circles
  //JOIN
  circles = circles.data(data, function(d) {
    return d.text
  })

  //EXIT
  circles.exit().remove()

  //ENTER
  circles = circles
    .enter().append("circle")
    .merge(circles)

  circles
    .transition(t)
    .attr("class", "node")
    .attr("r", function(d) {
      return radiusScale(d.size)
    })
    .style("fill", function(d) {
      return color((d.text[0].charCodeAt()))
    })

  circles
  	.on("mouseover", function(d) {
        tooltip.html("Word: " + d.text + "<br>Frequency: " + d.size)
        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");
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended))

  //For texts
  //JOIN
  texts = texts.data(data, function(d) {
    return d.text
  })

  //EXIT
  texts.exit().remove()

  //ENTER
  texts = texts
    .enter().append("text")
    .merge(texts)

  texts
    .transition(t)
    .attr("class", "text")
    .text(function(d) {
      return d.text
    })

  simulation.nodes(data)
  simulation.alpha(1).restart();
}

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

  texts
    .attr("x", function(d) {
      return d.x
    })
    .attr("y", function(d) {
      return d.y
    })

}

// make circles dragable
function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
  tooltip.style("visibility", "hidden");
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
&#13;
.node:hover {
  stroke: #000;
  stroke-width: 2px;
  opacity: .7;
}
&#13;
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="//d3js.org/d3-scale-chromatic.v0.3.min.js"></script>

<div id="slider"></div>
<div id="chart"></div>
&#13;
&#13;
&#13;

0 个答案:

没有答案