如何在d3.js图中转换多行?

时间:2016-06-21 18:21:49

标签: javascript d3.js transitions updating

我一直在尝试调整Mike Bostock的chained transition script来处理多行,但是我没有让它工作。在第一次显示之后,线条和标签飞出绘图并且不再显示所有内容都会更新(我可以在检查javascript控制台时看到线条的值发生变化)。我不明白我做错了什么。我将在下面发布(冗长的)代码(道歉为长度)。我将不胜感激任何帮助,谢谢!

<!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>

1 个答案:

答案 0 :(得分:1)

我可以帮助修复你的过渡,但我不确定你是在试图“连锁”。在链接示例中,Bostock将一行换成另一行(转换1),然后将该行与新域匹配(转换2)。您似乎不想交换行,因此您适合新域,然后将行转换为它(转换1)但转换2是什么?

现在回答您更直接的问题,即为什么转换不起作用,这只是因为您永远不会更新您的数据。在链接的示例中,Bostock将两个数据集绑定到他的行,然后交换他在行函数中绘制的数据集。但是,您只能绑定原始数据集。快速解决方法是:

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) + ")";
    })

}

运行代码:

<!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>