Joe1314
Joe1314

Reputation: 35

D3 bubble chart / force layout forceAttraction

I am trying to create bubble chart.(I'm new to D3.js).

First, I tried to change the code by referring to this site(https://www.d3-graph-gallery.com/graph/circularpacking_drag.html) to make a bubble chart. the following code is original one.

// set the dimensions and margins of the graph
var width = 450
var height = 450

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", 450)
  .attr("height", 450)

// create dummy data -> just one element per circle
var data = [{
  "name": "A"
}, {
  "name": "B"
}, {
  "name": "C"
}, {
  "name": "D"
}, {
  "name": "E"
}, {
  "name": "F"
}, {
  "name": "G"
}, {
  "name": "H"
}]

// Initialize the circle: all located at the center of the svg area
var node = svg.append("g")
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 25)
  .attr("cx", width / 2)
  .attr("cy", height / 2)
  .style("fill", "#19d3a2")
  .style("fill-opacity", 0.3)
  .attr("stroke", "#b3a2c8")
  .style("stroke-width", 4)
  .call(d3.drag() // call specific function when circle is dragged
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

// Features of the forces applied to the nodes:
var simulation = d3.forceSimulation()
  .force("center", d3.forceCenter().x(width / 2).y(height / 2)) // Attraction to the center of the svg area
  .force("charge", d3.forceManyBody().strength(1)) // Nodes are attracted one each other of value is > 0
  .force("collide", d3.forceCollide().strength(.1).radius(30).iterations(1)) // Force that avoids circle overlapping

// Apply these forces to the nodes and update their positions.
// Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
simulation
  .nodes(data)
  .on("tick", function(d) {
    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
  });

// What happens when a circle is dragged?
function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(.03).restart();
  d.fx = d.x;
  d.fy = d.y;
}

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

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(.03);
  d.fx = null;
  d.fy = null;
}
<div id="my_dataviz"></div>
<script src="https://d3js.org/d3.v5.js"></script>

And in this chart, since there was no attraction function(attract to center), I tried to add the following code. (I referred to the following site https://blockbuilder.org/ericsoco/d2d49d95d2f75552ac64f0125440b35e)

.force('attract', d3.forceAttract()
.target([width/2, height/2])
.strength(0.01))

However, it is not working.And it changes like the following image. enter image description here

Could anyone advice me why this happen?

Upvotes: 0

Views: 915

Answers (1)

Ruben Helsloot
Ruben Helsloot

Reputation: 13129

The image you get from d3.forceAttract not existing in d3 v5, as you can see from the console. You can use something like d3.forceRadial, however to add an attraction towards the center:

// set the dimensions and margins of the graph
var width = 450
var height = 450

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", 450)
  .attr("height", 450)

// create dummy data -> just one element per circle
var data = [{
  "name": "A"
}, {
  "name": "B"
}, {
  "name": "C"
}, {
  "name": "D"
}, {
  "name": "E"
}, {
  "name": "F"
}, {
  "name": "G"
}, {
  "name": "H"
}]

// Initialize the circle: all located at the center of the svg area
var node = svg.append("g")
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 25)
  .attr("cx", width / 2)
  .attr("cy", height / 2)
  .style("fill", "#19d3a2")
  .style("fill-opacity", 0.3)
  .attr("stroke", "#b3a2c8")
  .style("stroke-width", 4)
  .call(d3.drag() // call specific function when circle is dragged
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

// Features of the forces applied to the nodes:
var simulation = d3.forceSimulation()
  .force("center", d3.forceCenter().x(width / 2).y(height / 2)) // Attraction to the center of the svg area
  .force("charge", d3.forceManyBody().strength(1)) // Nodes are attracted one each other of value is > 0
  .force("collide", d3.forceCollide().strength(.1).radius(30).iterations(1)) // Force that avoids circle overlapping
  .force('attract', d3.forceRadial(0, width / 2, height / 2).strength(0.05))

// Apply these forces to the nodes and update their positions.
// Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
simulation
  .nodes(data)
  .on("tick", function(d) {
    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
  });

// What happens when a circle is dragged?
function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(.03).restart();
  d.fx = d.x;
  d.fy = d.y;
}

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

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(.03);
  d.fx = null;
  d.fy = null;
}
<div id="my_dataviz"></div>
<script src="https://d3js.org/d3.v5.js"></script>

Upvotes: 1

Related Questions