简体   繁体   中英

Transitions in D3 on load

I'm just starting out with D3, and I'm trying to create a simple bar chart that transitions when it loads. I've tried adding .transition() between every selection and attribute change in my code, and it makes the entire visual disappear. Here's my code more or less in its entirety:

<style>
.chart rect {
  fill: steelblue;
}

.chart text {
  fill: white;
  font: 10px sans-serif;
  text-anchor: end;
}

</style>
<svg class="chart"></svg>
<script src="js/d3.min.js"></script>
<script>

var width = 1000,
barHeight = 20;

var x = d3.scale.linear()
.range([0, width]);

var chart = d3.select(".chart")
.attr("width", width);

d3.csv("data2.csv", type, function(error, data) {
  x.domain([0, d3.max(data, function(d) { return d.value; })]);

  chart.attr("height", barHeight * data.length);

  var bar = chart.selectAll("g")
      .data(data)
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });

  bar.append("rect")
      .attr("width", function(d) { return x(d.value); })
      .attr("height", barHeight - 1);

  bar.append("text")
      .attr("x", function(d) { return x(d.value) - 3; })
  .attr("y", barHeight / 2)
  .attr("dy", ".35em")
  .text(function(d) { return d.value; });
});

function type(d) {
  d.value = +d.value; // coerce to number
  return d;
}

</script>

Not really sure if it's a more fundamental problem with my code, but every resource I've read makes it seem like adding .transition() should be an easy change to make, and I haven't found that to be the case. Any answers are appreciated.

I might have a solution. This might be more of a workaround; I'm kind of new to this as well, and this is just a solution that has worked for me.

The solution I've found is to have two sets of data. What you need to do is create a blank set of data — data that has the same length of values, but have them all set to 0 — that gets rendered first. So create a global variable like so:

//create before passing data
var emptyVar  = 0;

D3 returns the data as an array of objects. So, you'll need to create an empty array as well:

var emptyData = [];

The remaining steps take place once the data has been parsed in your "type" function (or while). Within the code you're passing the csv, you'll have to fill in your empty data set with your empty variables. Do this, you'll create a for() statement and pass your imported data as the length of the emptyData's length. That way, your emptyData will be filled with just as many emptyVars as there are variables within your imported data. You do that like so:

d3.csv("data2.csv", type, function(error, data) {
x.domain([0, d3.max(data, function(d) { return d.value; })]);

for(var i = 0;i<data.length;i++){
  emptyData[i] = {value:empty[i]};
}

Unfortunately, this will return undefined variables, so if you console log emptyData[0].value (note: the data is an array of objects, not just an array. That's how the csv is interpreted, so that's how we're building our empty array) you'll get "undefined". To remedy this, you'll have to pass an if statement that converts all "undefined"s into a 0. That code looks like this:

for(i = 0;i<emptyData.length;i++){
  if(emptyData[i].value === undefined){
    emptyData[i].value = 0;
  }
}

If done in the order, you'll have an emptyData set — where all of the values are 0 — that's just as long as your initial, imported csv data set.

Now that you have an empty set of data, you can tell D3 to create your bar chart based on the blank data. To fake the "onload" portion of this question, you can then set the second, actual data to load the proper chart by setting an on "mouseover" event to the entire body of your document. In that sense, there's not technically an onload set, but it will come across as if the chart transitions with the page load.

To do this, first create your chart with your existing code, but pass your emptyData:

  chart.attr("height", barHeight * data.length);

  var bar = chart.selectAll("g")
      .data(emptyData)
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });

  bar.append("rect")
      .attr("width", function(d) { return x(d.value); })
      .attr("height", barHeight - 1);

  bar.append("text")
      .attr("x", function(d) { return x(d.value) - 3; })
  .attr("y", barHeight / 2)
  .attr("dy", ".35em")
  .text(function(d) { return d.value; });

That'll create a chart based on your empty data. Now, create the mouseover event with your actual data (csv data) being passed through. That'll look like this:

d3.select("body")
  .on("mouseover",function (){

chart.selectAll(".bar")
    .data(data)
    .transition()
    .duration(500)
    .ease("linear")
//the attributes you'd like to change are listed below
    .attr("fill","#5489c7")
    .attr("width", function(d) { return x(d.value); })
    .attr("height", barHeight - 1);
//change the text attributes as well within here
        ...
//close off the function when you're finished
});

That should be it. It's not exactly an onload event like you asked, but it should fake it enough to seem like an onload event. Hopefully, that works for you. It might take some finessing to get it to work; the important thing is to remember how to pass the empty data. That was half the battle with getting it to work on my end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM