【问题标题】:How to transition multiple lines in d3.js plot?如何在 d3.js 图中转换多行?
【发布时间】:2016-10-23 10:32:51
【问题描述】:

我一直在尝试调整 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>

【问题讨论】:

    标签: javascript d3.js transitions updating


    【解决方案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>

    【讨论】:

      猜你喜欢
      • 2016-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-17
      • 1970-01-01
      相关资源
      最近更新 更多