d3.js条形图转换无法正常工作

d3.js Bar Chart transitions not working properly

本文关键字:常工作 工作 js 条形图 转换 d3      更新时间:2023-09-26

我这里有一个简单的条形图,当通过按钮点击事件提交新数据时,它应该很好地将所有的条形图稍微向左转换,为新的条形图在图的右侧显示留出空间,但它们似乎都会向左侧叠加。

这是我的转换代码:

bars.transition()
        .duration(1000)
        .attr("x", function(d, i) {
          return x(i);
        })
        .attr("y", function(d) {
          return y(d.count);
        })
        .attr("width", x.rangeBand())
        .attr("height", function(d) {
          return height - y(d.count);
        });

以下是迄今为止的代码:https://jsfiddle.net/foL2Lcg1/1/

在我使用的按钮提交中输入的测试数据是"Service:blah"answers"Count:60000",您可以在图表下方输入。

有什么建议吗?

从这里的示例来看:https://bl.ocks.org/RandomEtc/cff3610e7dd47bef2d01

我用你的数据创建了这个小提琴:https://jsfiddle.net/thatOneGuy/foL2Lcg1/5/

我添加了一个绘图函数,用于传递数据。因此,您不再像以前那样重写代码,而是从调用这个函数开始,添加/删除一些数据后,使用新的数据集再次调用这个函数,它就会更新。以下是功能:

function draw(data) {
  // measure the domain (for x, unique letters) (for y [0,maxFrequency])
  // now the scales are finished and usable
  x.domain(data.map(function(d) { return d.service; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]);
  // another g element, this time to move the origin to the bottom of the svg element
  // someSelection.call(thing) is roughly equivalent to thing(someSelection[i])
  //   for everything in the selection'
  // the end result is g populated with text and lines!
  svg.select('.x.axis').transition().duration(300).call(xAxis);
  // same for yAxis but with more transform and a title
  svg.select(".y.axis").transition().duration(300).call(yAxis)
  // THIS IS THE ACTUAL WORK!
  var bars = svg.selectAll(".bar").data(data) // (data) is an array/iterable thing, second argument is an ID generator function
  bars.exit()
    .transition()
      .duration(300)
    .attr("y", y(0))
    .attr("height", height - y(0))
    .style('fill-opacity', 1e-6)
    .remove();
  // data that needs DOM = enter() (a set/selection, not an event!)
  bars.enter().append("rect")
    .attr("class", "bar")
     .attr("y", function(d) {
          return y(d.count);
        })
   .attr("height", function(d) {
          return height - y(d.count);
        })

  // the "UPDATE" set:
  bars.transition().duration(300).attr("x", function(d) { return x(d.service); }) // (d) is one item from the data array, x is the scale object from above
    .attr("width", x.rangeBand()) // constant, so no callback function(d) here
    .attr("y", function(d) { return y(d.count); })
    .attr("height", function(d) { return height - y(d.count); }); // flip the height, because y's domain is bottom up, but SVG renders top down
}

现在更新,就像你在按钮点击:

d3.select("button")
      .attr("id", "submit")
      .on("click", function() {
          var service = document.getElementById("service").value;
          var count = document.getElementById("count").value;
          var json = {
            "count": count,
            "service": service
          };
          data.push(json);
          draw(data)
          })

如果fiddle不工作,下面是工作代码:

var data = [
      {
        service: "QA",
        count: "25262"
      }, {
        service: "QB",
        count: "42386"
      }, {
        service: "QUERY_NOTICICATIONS",
        count: "14042"
      }, {
        service: "TTL",
        count: "4088"
      }
    ];
// Mike Bostock "margin conventions"
var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = window.innerWidth - margin.left - margin.right,
    height = window.innerHeight/1.5 - margin.top - margin.bottom;
// D3 scales = just math
// x is a function that transforms from "domain" (data) into "range" (usual pixels)
// domain gets set after the data loads
var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
    .range([height, 0]);
// D3 Axis - renders a d3 scale in SVG
var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");
var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    //.ticks(10, "%");
// create an SVG element (appended to body)
// set size
// add a "g" element (think "group")
// annoying d3 gotcha - the 'svg' variable here is a 'g' element
// the final line sets the transform on <g>, not on <svg>
var svg = d3.select(".chart") 
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
svg.append("g")
    .attr("class", "y axis")
  .append("text") // just for the title (ticks are automatic)
    .attr("transform", "rotate(-90)") // rotate the text!
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Frequency");
// d3.tsv is a wrapper around XMLHTTPRequest, returns array of arrays (?) for a TSV file
// type function transforms strings to numbers, dates, etc.
 draw(data);
function type(d) {
  // + coerces to a Number from a String (or anything)
  d.count = +d.count;
  return d;
}
 
function draw(data) {
  // measure the domain (for x, unique letters) (for y [0,maxFrequency])
  // now the scales are finished and usable
  x.domain(data.map(function(d) { return d.service; }));
  y.domain([0, d3.max(data, function(d) { return d.count; })]);
  // another g element, this time to move the origin to the bottom of the svg element
  // someSelection.call(thing) is roughly equivalent to thing(someSelection[i])
  //   for everything in the selection'
  // the end result is g populated with text and lines!
  svg.select('.x.axis').transition().duration(300).call(xAxis);
  // same for yAxis but with more transform and a title
  svg.select(".y.axis").transition().duration(300).call(yAxis)
  // THIS IS THE ACTUAL WORK!
  var bars = svg.selectAll(".bar").data(data) // (data) is an array/iterable thing, second argument is an ID generator function
  bars.exit()
    .transition()
      .duration(300)
    .attr("y", y(0))
    .attr("height", height - y(0))
    .style('fill-opacity', 1e-6)
    .remove();
  // data that needs DOM = enter() (a set/selection, not an event!)
  bars.enter().append("rect")
    .attr("class", "bar")
     .attr("y", function(d) {
          return y(d.count);
        })
   .attr("height", function(d) {
          return height - y(d.count);
        })
    
       
  // the "UPDATE" set:
  bars.transition().duration(300).attr("x", function(d) { return x(d.service); }) // (d) is one item from the data array, x is the scale object from above
    .attr("width", x.rangeBand()) // constant, so no callback function(d) here
    .attr("y", function(d) { return y(d.count); })
    .attr("height", function(d) { return height - y(d.count); }); // flip the height, because y's domain is bottom up, but SVG renders top down
}
d3.select("button")
      .attr("id", "submit")
      .on("click", function() {
        
          var service = document.getElementById("service").value;
          var count = document.getElementById("count").value;
          var json = {
            "count": count,
            "service": service
          };
          data.push(json);
          draw(data)
          })
#tooltip {
  position: absolute;
  width: 200px;
  height: auto;
  padding: 10px;
  background-color: white;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}
#tooltip.hidden {
  display: none;
}
#tooltip p {
  margin: 0;
  font-family: sans-serif;
  font-size: 16px;
  line-height: 20px;
}
.chart rect {
  fill: steelblue;
}
.chart text {
  font: 10px sans-serif;
  text-anchor: end;
}
.axis text {
  font: 10px sans-serif;
}
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
svg text {
  pointer-events: none;
}
rect {
  -moz-transition: all 0.3s;
  -o-transition: all 0.3s;
  -webkit-transition: all 0.3s;
  transition: all 0.3s;
}
rect:hover {
  fill: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
  <svg class="chart"></svg>
  <div class="input">
    <p>Service :
      <input id="service" type="text"></input> Count :
      <input id="count" type="text"></input>
    </p>
    <br/>
    <button id="submit">Submit</button>
  </div>
  <div id="tooltip" class="hidden">
    <p><strong>Tooltip</strong></p>
    <p><span id="service"></span></p>
    <p><span id="count"></span></p>
  </div> 
</body>

您的代码中有一个小错误。使用x(d.service)而不是x(i)来计算点击函数中条的x位置。

 bars.transition()
         .duration(1000)
         .attr("x", function(d, i) {
           return x(d.service); //instead of x(i);
         })
         .attr("y", function(d) {
           return y(d.count);
         })
         .attr("width", x.rangeBand())
         .attr("height", function(d) {
           return height - y(d.count);
         });

var data = [
     {
       service: "QA",
       count: "25262"
     }, {
       service: "QB",
       count: "42386"
     }, {
       service: "QUERY_NOTICICATIONS",
       count: "14042"
     }, {
       service: "TTL",
       count: "4088"
     }
   ];
   var width = 960,
     barHeight = 500;
   var margin = {
       top: 20,
       right: 30,
       bottom: 50,
       left: 60
     },
     width = 960 - margin.left - margin.right,
     height = 500 - margin.top - margin.bottom;
   var x = d3.scale.ordinal()
     .rangeRoundBands([0, width], .1);
   var y = d3.scale.linear()
     .range([height, 0]);
   var xAxis = d3.svg.axis()
     .scale(x)
     .orient("bottom");
   var yAxis = d3.svg.axis()
     .scale(y)
     .orient("left");
   var chart = d3.select(".chart")
     .attr("width", width + margin.right + margin.left)
     .attr("height", height + margin.top + margin.bottom)
     .append("g")
     .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
   x.domain(data.map(function(d) {
     return d.service;
   }));
   y.domain([0, d3.max(data, function(d) {
     return d.count
   })]);
   chart.append("g")
     .attr("class", "x axis")
     .attr("transform", "translate(0," + height + ")")
     .call(xAxis);
   chart.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("Count");
   chart.selectAll(".bar")
     .data(data)
     .enter().append("rect")
     .attr("class", "bar")
     .attr("x", function(d) {
       return x(d.service);
     })
     .attr("y", function(d) {
       return y(d.count);
     })
     .attr("height", function(d) {
       return height - y(d.count);
     })
     .attr("width", x.rangeBand())
     .on("mouseover", function(d) {
       var xPos = parseFloat(d3.select(this).attr("x")) + x.rangeBand() / 2;
       var yPos = parseFloat(d3.select(this).attr("y")) / 2 + height / 2;
       //update tooltip positiom
       d3.select("#tooltip")
         .style("left", xPos + "px")
         .style("top", yPos + "px")
         .select("#service")
         .text("Service: " + d.service);
       d3.select("#tooltip")
         .style("left", xPos + "px")
         .style("top", yPos + "px")
         .select("#count")
         .text("Count: " + d.count);
       //show tooltip
       d3.select("#tooltip").classed("hidden", false);
     })
     .on("mouseout", function() {
       d3.select("#tooltip").classed("hidden", true);
     });

   d3.select("button")
     .attr("id", "submit")
     .on("click", function() {
       var service = document.getElementById("service").value;
       var count = document.getElementById("count").value;
       var json = {
         "count": count,
         "service": service
       };
       data.push(json);
       console.log(data);
       x.domain(data.map(function(d) {
         return d.service;
       }));
       y.domain([0, d3.max(data, function(d) {
         return d.count
       })]);
       chart.select(".x.axis")
         .transition()
         .duration(1000)
         .call(xAxis);
       chart.select(".y.axis")
         .transition()
         .duration(1000)
         .call(yAxis);
       var bars = chart.selectAll(".bar")
         .data(data);
       bars.enter().append("rect");
       bars.attr("class", "bar")
         .attr("x", function(d) {
           return x(d.service);
         })
         .attr("y", function(d) {
           return y(d.count);
         })
         .attr("height", function(d) {
           return height - y(d.count);
         })
         .attr("width", x.rangeBand())
         .on("mouseover", function(d) {
           var xPos = parseFloat(d3.select(this).attr("x")) + x.rangeBand() / 2;
           var yPos = parseFloat(d3.select(this).attr("y")) / 2 + height / 2;
           //update tooltip positiom
           d3.select("#tooltip")
             .style("left", xPos + "px")
             .style("top", yPos + "px")
             .select("#service")
             .text("Service: " + d.service);
           d3.select("#tooltip")
             .style("left", xPos + "px")
             .style("top", yPos + "px")
             .select("#count")
             .text("Count: " + d.count);
           //show tooltip
           d3.select("#tooltip").classed("hidden", false);
         })
         .on("mouseout", function() {
           d3.select("#tooltip").classed("hidden", true);
         });
       bars.transition()
         .duration(1000)
         .attr("x", function(d, i) {
           return x(d.service);
         })
         .attr("y", function(d) {
           return y(d.count);
         })
         .attr("width", x.rangeBand())
         .attr("height", function(d) {
           return height - y(d.count);
         });


     })
#tooltip {
  position: absolute;
  width: 200px;
  height: auto;
  padding: 10px;
  background-color: white;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}
#tooltip.hidden {
  display: none;
}
#tooltip p {
  margin: 0;
  font-family: sans-serif;
  font-size: 16px;
  line-height: 20px;
}
.chart rect {
  fill: steelblue;
}
.chart text {
  font: 10px sans-serif;
  text-anchor: end;
}
.axis text {
  font: 10px sans-serif;
}
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
svg text {
  pointer-events: none;
}
rect {
  -moz-transition: all 0.3s;
  -o-transition: all 0.3s;
  -webkit-transition: all 0.3s;
  transition: all 0.3s;
}
rect:hover {
  fill: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg class="chart"></svg>
<div class="input">
  <p>Service :
    <input id="service" type="text" />Count :
    <input id="count" type="text" />
  </p>
  <br/>
  <button id="submit">Submit</button>
</div>
<div id="tooltip" class="hidden">
  <p><strong>Tooltip</strong>
  </p>
  <p><span id="service"></span>
  </p>
  <p><span id="count"></span>
  </p>
</div>