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

时间:2016-05-12 08:40:08

标签: javascript jquery html css d3.js

我在这里有一个简单的条形图,可以很好地将所有条形图稍微向左移动,以便在通过按钮点击事件提交新数据时为图形右侧的新条形图留出空间,但是它们是所有人似乎都互相攻击左侧。

以下是我的过渡代码:

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

到目前为止,这是代码的jsfiddle:https://jsfiddle.net/foL2Lcg1/1/

我在使用按钮提交时输入的测试数据是' Service:blah'和'伯爵:60000'您可以在图表下方输入。

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

来自此处的示例: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)
          })

如果小提琴不起作用,这是工作代码:



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)
          })
&#13;
#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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

您的代码中存在一个小错误。使用x(i)代替 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); }); 来计算点击函数中条形的x位置。

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>
{{1}}