D3.js更新模式混乱

时间:2017-09-04 16:09:04

标签: json d3.js

我有一个基本的条形图。 Bootstrap下拉菜单选择data1.json或data2.json中的数据。两者都只有键“A”,“B”,“C”和“D”。这意味着没有任何条形图正在退出或输入数据,它们都只是更新值。

我有用于为条形指定颜色的类,以便那些退出,更新和输入分别为红色,橙色和绿色。由于值仅更新,因此所有条形应仅为橙色。这是“A”的行为。但其他人变得“红色”,好像他们正在退出数据,而“绿色”就好像他们是新的输入数据一样。

为什么栏“B”,“C”和“D”的行为与栏“A”不同?对我而言“A”是正确的。

不幸的是,此代码段不起作用。我无法让它在jsFiddle中工作。

但它位于github并且正在gh-pages上运行。

使用下拉菜单更改数据。

data1.json

[{
    "name": "A",
    "value": 5
  },
  {
    "name": "B",
    "value": 3
  },
  {
    "name": "C",
    "value": 2
  },
  {
    "name": "D",
    "value": 4
  }
]

data2.json

[{
    "name": "A",
    "value": 3
  },
  {
    "name": "B",
    "value": 7
  },
  {
    "name": "C",
    "value": 4
  },
  {
    "name": "D",
    "value": 8
  }
]

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .container {
    margin-left: 0px;
  }

  .exit {
    fill: red;
  }

  .update {
    fill: orange;
  }

  .enter {
    fill: green;
  }

  #json {
    max-height: 400px;
    width: 200px;
    overflow: scroll;
    border: 2px solid gray;
    border-radius: 15px;
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" crossorigin="anonymous"></script>

<body>
  <ul class="nav nav-tabs">
    <li class="nav-item">
      <a class="nav-link active" href="https://github.com/shanegibney/D3-v4-Bar-Chart-Update-Pattern">Home</a>
    </li>
    <li class="nav-item dropdown">
      <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Data</a>
      <div class="dropdown-menu">
        <a class="dropdown-item" onclick="updateFunction('1')" href="# ">data1.json</a>
        <a class="dropdown-item" onclick="updateFunction('2')" href="#">data2.json</a>
      </div>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="https://github.com/shanegibney/D3-v4-Bar-Chart-Update-Pattern">Link</a>
    </li>
    <li class="nav-item">
      <a class="nav-link disabled" href="#">Disabled</a>
    </li>
  </ul>
  <div class="container-fluid">
    <div class="row">

      <div class="col-md-8" id="race_graph">
      </div>
      <div id="json">
        <!-- <pre class="col-md-4" id="json">hello</pre> -->
      </div>
    </div>
  </div>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    // set the dimensions and margins of the graph
    var margin = {
        top: 70,
        right: 50,
        bottom: 100,
        left: 80
      },
      width = 1000 - margin.left - margin.right,
      height = 600 - margin.top - margin.bottom;

    var data1 = [{
        "name": "A",
        "value": 5
      },
      {
        "name": "B",
        "value": 3
      },
      {
        "name": "C",
        "value": 2
      },
      {
        "name": "D",
        "value": 4
      }
    ]
    var data2 = [{
        "name": "A",
        "value": 3
      },
      {
        "name": "B",
        "value": 7
      },
      {
        "name": "C",
        "value": 4
      },
      {
        "name": "D",
        "value": 8
      }
    ]

    // set the domains and ranges
    var x = d3.scaleBand()
      .range([0, width])
      .padding([0.6]);

    // temporal y-scale
    var y = d3.scaleLinear()
      .range([height, 0]);

    var xAxis = d3.axisBottom(x);

    var yAxis = d3.axisLeft(y)
      .ticks(8);

    // Add main graph svg
    var svg = d3.select("#race_graph")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom);

    // Add groups for main bar chart
    var g0 = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var index = 1;
    updateFunction(index);

    function updateFunction(index) {
      // d3.json("data" + index + ".json", function(error, newdata) {
      newdata = "data" + index;
      // if (error) throw error;
      data = newdata;

      data.forEach(function(d) {
        d.value = +d.value;
      })

      // To display json in html page
      document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";

      update();
      // });
    }

    function update(err, newdata) {

      // set the domains
      x.domain(data.map(function(d) {
        return d.name
      }));

      // set domain temporal y-scale
      y.domain([0, d3.max(data, function(d) {
        return d.value;
      })]).nice();

      // Add the X Axis
      svg.select(".x.axis").call(xAxis);
      svg.select(".x.axis").remove();
      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(" + (margin.left) + "," + (height + margin.top) + ")")
        .call(xAxis);

      // Add the y axis on left
      svg.select(".y.axis").remove();
      svg.append("g")
        .attr("class", "y axis")
        .attr("transform", "translate(" + (margin.left) + "," + margin.top + ")")
        .call(yAxis);

      var t = d3.transition()
        .duration(750);

      // JOIN new data with old elements
      var newRects0 = g0.selectAll(".bar")
        .data(data, function(d) {
          return d;
        });

      // EXIT old elements not present in new data
      newRects0.exit()
        .attr("class", function(d) {
          return "exit bar";
        }) // fill red
        .transition(d3.transition()
          // .delay(0)
          .duration(750))
        .attr("y", function(d) {
          return height;
        }) // old elements which are leaving the chart, their y position transitions to the xaxis
        .attr("height", function(d) {
          return 0;
        }) // old elements which are leaving the chart, therir height trasnitions to 0
        .remove();

      // UPDATE old elements present in new data
      newRects0.attr("class", function(d) {
          return "update bar";
        })
        .transition(d3.transition()
          .delay(1000))
        .duration(750)
        .attr("x", function(d) {
          return x(d.name);
        }) // old elememnts in new data transition to their new position
        .attr("y", function(d) {
          return y(d.value)
        }) // old elememnts in new data transition to their y position
        .attr("height", function(d, i) {
          return height - y(d.value)
        }); // old elememnts in new data transition to their correct height

      // ENTER new elements present in new data
      // EXIT and UPDATE above will not apply first time render as there is no change to the data.
      newRects0.enter()
        .append('rect')
        .attr("class", "enter bar") // fill green
        .attr("x", function(d) {
          return x(d.name);
        })
        .attr("y", height) // bars start on xaxis or position y=height
        .attr("height", function(d) {
          return 0;
        }) // bars start with zero height
        .attr("width", x.bandwidth())
        .transition(d3.transition()
          .delay(2000))
        .duration(750)
        .attr("y", function(d, i) {
          return y(d.value);
        }) // after the transition bars goes to its correct y position
        .attr("height", function(d, i) {
          return height - y(d.value)
        }); // after transition bar grows to its full height

    }; // end of update() function
  </script>
</body>

谢谢,

1 个答案:

答案 0 :(得分:2)

您需要为data()函数提供一个保持不变的密钥,因此它知道哪些新数据与旧数据相同。 d.name应该比返回整个对象更好。这样的事情对我有用:

var newRects0 = g0.selectAll(".bar")
    .data(data, function(d) {
      return d.name;
    });

这是一个工作小提琴:https://jsfiddle.net/markm/d3c2g42L/