简体   繁体   中英

How to transition multiple lines in d3.js plot?

I have been trying to adapt the chained transition script of Mike Bostock to work with multiple lines but I do not get it to work. After the first display the lines and labels fly out of the plot and do not show anymore Whereas everything gets updated (I can see the values of the lines changing when inspecting the javascript console) . I do not understand what I am doing wrong. I will post the (lengthy) code here below (apologies for the length). I would appreciate any help, thank you!

<!DOCTYPE html>
<head>
<title>Modified Chained Transitions</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: auto;
  position: relative;
  width: 960px;
}

text {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}

form {
  position: absolute;
  right: 10px;
  top: 10px;
}

</style>
</head>
<body>
<br>
  <button type="button"> Request data</button>

  <div id='chart'> </div>
</body>
<script>

var margin = {top: 20, right: 80, bottom: 30, left: 50},
    width = 750 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y%m%d").parse;

var xScale = d3.time.scale()
    .range([0, width]);

var yScale = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left");

var line = d3.svg.line()
                .interpolate("basis")
                .x(function(d) { return xScale(d.date); })
                .y(function(d) { return yScale(d.temperature); });

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


var getNewData = function() {
    var data = [];
    var counter = 0;
  function generate(){
        var startDate = new Date;
      counter += 1;
        var range = counter % 2 === 0 ? 10 : 100; 
        for (i = 0; i < 100; i++) {
            data[i] = {"date": new Date(startDate - i),
                "New York": Math.random() * (range - 1), 
                "San Francisco": Math.random() * (range - 1),
                "Austin": Math.random() * (range - 10)};
        }
        return data;
    } 
    return {
        new: function () {return generate()}
    };
}; // function getNewData() 

var newData = getNewData();
data = newData.new();

color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));

var cities = color.domain().map(function(name) {
  return {
    name: name,
    values: data.map(function(d) {
      return { date: d.date, temperature: +d[name]};
    })
  };
});

xScale.domain(d3.extent(data, function(d) { return d.date; }));
yScale.domain([
  d3.min(cities, function(c) {
    return d3.min(c.values, function(v) { return v.temperature; }); }),
  d3.max(cities, function(c) {
    return d3.max(c.values, function(v) { return v.temperature; }); })
]);

svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
    .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Temperature (ºF)");

var city = svg.selectAll(".city")
    .data(cities)
    .enter().append("g")
    .attr("class", "city");

city.append("path")
    .attr("class", "line")
    .attr("d", function(d) { return line(d.values); })
    .style("stroke", function(d) { return color(d.name); });

city.append("text")
    .datum(function(d) { return {name: d.name, values: d.values[0]}; })
        .attr("class", "label")
    .attr("transform", function(d) { return "translate(" +
      xScale(d.values.date) +  "," + yScale(d.values.temperature) + ")"; })
    .attr("x", 3)
    .attr("dy", ".35em")
    .text(function(d) { return d.name; });

d3.selectAll("button").on("click", change);

function change() {

    data = newData.new();
    color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
    cities = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return { date: d.date, temperature: +d[name]};
        })
      };
    });
    console.log(cities[0].values[0]);

    xScale.domain(d3.extent(data, function(d) { return d.date; }));
    yScale.domain([
      d3.min(cities, function(c) {
        return d3.min(c.values, function(v) { return v.temperature; }); }),
      d3.max(cities, function(c) {
        return d3.max(c.values, function(v) { return v.temperature; }); })
    ]);

    var t0 = svg.transition().duration(750);
    t0.selectAll(".line")
        .attr("d", function(cities) { return line(cities.values); })
        .style("stroke", function(cities) { return color(cities.name); });
    t0.selectAll(".label").attr("transform", 
         "translate(0,0)").text(function(cities) { return cities.name; });


    var t1 = t0.transition();
//  t1.selectAll(".line").attr("d", line(data));
//  t1.select(".line")
    t1.selectAll(".line")
//      t1.selectAll(".city")
        .attr("d", function(cities) { return line(cities.values); })
        .style("stroke", function(cities) { return color(cities.name); });
    t1.select(".y.axis").call(yAxis);
    t1.select(".x.axis").call(xAxis);
    t1.select(".label")
        .attr("transform", function(d) { return "translate(" +
      xScale(d.values.date) +  "," + 
            yScale(d.values.temperature) + ")"; });
} // function change() 

</script>
</html>

I can help fix your transitions but I'm not sure what you are attempting to "chain". In the linked example, Bostock swaps one line for another (transition 1), then fits that line to a new domain (transition 2). You do not seem to want to swap lines, so you fit a new domain and then transition the lines to it (transition 1) but what's transition 2?

Now to answer your more direct question of why your transitions aren't working, it's simply because you never update your data. In the linked example Bostock has both datasets bound to his line and then swaps which he's drawing in the line function. You, though, only ever have the original dataset bound. Quick fix is:

function change() {

  ... //<-- get new data

  // bind your new data
  var cities = svg.selectAll(".city")
    .data(cities)

  // sub selection to transition line   
  cities
    .select(".line")
    .transition()
    .duration(750)
    .attr("d", function(d) { return line(d.values); })
    .style("stroke", function(d) { return color(d.name); })

  // concurrent sub selection to move labels
  cities
    .select(".label")
    .transition()
    .duration(750)
    .attr("transform", function(d){
       var last = d.values[0];
       return "translate(" + xScale(last.date) + "," +   yScale(last.temperature) + ")";
    })

}

Running code:

 <!DOCTYPE html> <head> <title>Modified Chained Transitions</title> <meta charset="utf-8"> <script src="https://d3js.org/d3.v3.min.js"></script> <style> body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; margin: auto; position: relative; width: 960px; } text { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .x.axis path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } form { position: absolute; right: 10px; top: 10px; } </style> </head> <body> <br> <button type="button"> Request data</button> <div id='chart'> </div> </body> <script> var margin = { top: 20, right: 80, bottom: 30, left: 50 }, width = 500 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y%m%d").parse; var xScale = d3.time.scale() .range([0, width]); var yScale = d3.scale.linear() .range([height, 0]); var color = d3.scale.category10(); var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); var line = d3.svg.line() .interpolate("basis") .x(function(d) { return xScale(d.date); }) .y(function(d) { return yScale(d.temperature); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var getNewData = function() { var data = []; var counter = 0; function generate() { var startDate = new Date; counter += 1; var range = counter % 2 === 0 ? 10 : 100; for (i = 0; i < 100; i++) { data[i] = { "date": new Date(startDate - i), "New York": Math.random() * (range - 1), "San Francisco": Math.random() * (range - 1), "Austin": Math.random() * (range - 10) }; } return data; } return { new: function() { return generate() } }; }; // function getNewData() var newData = getNewData(); data = newData.new(); color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; })); var cities = color.domain().map(function(name) { return { name: name, values: data.map(function(d) { return { date: d.date, temperature: +d[name] }; }) }; }); xScale.domain(d3.extent(data, function(d) { return d.date; })); yScale.domain([ d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }), d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); }) ]); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Temperature (ºF)"); var city = svg.selectAll(".city") .data(cities) .enter().append("g") .attr("class", "city"); city.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); city.append("text") .datum(function(d) { return { name: d.name, values: d.values[0] }; }) .attr("class", "label") .attr("transform", function(d) { return "translate(" + xScale(d.values.date) + "," + yScale(d.values.temperature) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }); d3.selectAll("button").on("click", change); function change() { data = newData.new(); color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; })); cities = color.domain().map(function(name) { return { name: name, values: data.map(function(d) { return { date: d.date, temperature: +d[name] }; }) }; }); xScale.domain(d3.extent(data, function(d) { return d.date; })); yScale.domain([ d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }), d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); }) ]); var cities = svg.selectAll(".city") .data(cities) cities .select(".line") .transition() .duration(750) .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }) cities .select(".label") .transition() .duration(750) .attr("transform", function(d) { var last = d.values[0]; return "translate(" + xScale(last.date) + "," + yScale(last.temperature) + ")"; }) svg.selectAll(".y.axis") .transition() .duration(750) .call(yAxis); svg.selectAll(".x.axis") .transition() .duration(750) .call(xAxis); } // function change() </script> </html> 

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