Home > Net >  d3 change bar chart color according to height
d3 change bar chart color according to height

Time:06-15

I created a bar chart in a way that I can expand or reduce the height of every bar with mouse drag and it changes the data automatically according to the dragged value. I want to change the color in a way that every bar gets more red when I expand its height and more green when I reduce its height. How can I achieve it ?

<!DOCTYPE html>
<html>
  <style>
    .selection {
      fill: steelblue;
      fill-opacity: 1;
    }

    body {
      width: 80%;
      margin: auto;
    }
  </style>
  <body >
    <script src="https://d3js.org/d3.v4.min.js"></script>

    <script>
      var data = [
        { index: 0, value: 18 },
        { index: 1, value: 20 },
        { index: 2, value: 19 },
      ];

      var widthY = 550,
        heightY = 600,
        delim = 4;

      var scaleY = d3.scaleLinear().domain([0, 21]).rangeRound([heightY, 0]);

      var x = d3.scaleLinear().domain([0, data.length]).rangeRound([0, widthY]);

      var svgY = d3
        .select("body")
        .append("svg")
        .attr("width", widthY)
        .attr("height", heightY)
        .attr("fill", "green")
        .append("g");

      var brushY = d3
        .brushY()
        .extent(function (d, i) {
          return [
            [x(i)   delim / 2, 0],
            [x(i)   x(1) - delim / 2, heightY],
          ];
        })
        .on("brush", brushmoveY)
        .on("end", brushendY);

      var svgbrushY = svgY
        .selectAll(".brush")
        .data(data)
        .enter()
        .append("g")
        .attr("class", "brush")
        .append("g")
        .call(brushY)
        .call(brushY.move, function (d) {
          return [d.value, 0].map(scaleY);
        });

      svgbrushY
        .append("text")
        .attr("y", function (d) {
          return scaleY(d.value)   25;
        })
        .attr("x", function (d, i) {
          return x(i)   x(0.5);
        })
        .attr("dx", "-.60em")
        .attr("dy", -5)
        .style("fill", "white")
        .text(function (d) {
          return d3.format(".2")(d.value);
        });

      function brushendY() {
        if (!d3.event.sourceEvent) return;
        if (d3.event.sourceEvent.type === "brush") return;
        if (!d3.event.selection) {
          svgbrushY.call(brushY.move, function (d) {
            return [d.value, 0].map(scaleY);
          });
        }
      }

      function brushmoveY() {
        if (!d3.event.sourceEvent) return;
        if (d3.event.sourceEvent.type === "brush") return;
        if (!d3.event.selection) return;

        var d0 = d3.event.selection.map(scaleY.invert);
        var d = d3.select(this).select(".selection");

        d.datum().value = d0[0];

        update();
      }

      function update() {
        svgbrushY
          .call(brushY.move, function (d) {
            return [d.value, 0].map(scaleY);
          })
          .selectAll("text")
          .attr("y", function (d) {
            return scaleY(d.value)   25;
          })
          .text(function (d) {
            return d3.format(".2")(d.value);
          });
      }
      
    </script>
  </body>
</html>

CodePudding user response:

First we need a scale:

 var color = d3.scaleLinear().domain(scaleY.domain()).range(["lightgreen","crimson"])

Now we need to update the color of the bars on drag in the update function:

 svgbrushY.selectAll(".selection").style("fill", function(d) { return color(d.value); })   

That gets us almost all the way there - but the colors are not right initially. The creation of the brush breaks the data binding for the .selection rectangle: if you log

svgbrushY.selectAll(".selection").each(function(d) { console.log(d); })

You'll notice that the datum doesn't have the value property before being dragged. So we can add it ourselves initially and style the bar the same as we do during the update function:

   svgbrushY.select(".selection")
     .datum(d=>d)
     .style("fill", function(d) { return color(d.value); })

That should give us something like:

<!DOCTYPE html>
<html>
  <style>
    .selection {
      fill-opacity: 1;
    }

    body {
      width: 80%;
      margin: auto;
    }
  </style>
  <body >
    <script src="https://d3js.org/d3.v4.min.js"></script>

    <script>
      var data = [
        { index: 0, value: 18 },
        { index: 1, value: 20 },
        { index: 2, value: 19 },
      ];

      var widthY = 550,
        heightY = 200,
        delim = 4;

      var scaleY = d3.scaleLinear().domain([0, 21]).rangeRound([heightY, 0]);

      var x = d3.scaleLinear().domain([0, data.length]).rangeRound([0, widthY]);
      
      var color = d3.scaleLinear().domain(scaleY.domain()).range(["lightgreen","crimson"])

      var svgY = d3
        .select("body")
        .append("svg")
        .attr("width", widthY)
        .attr("height", heightY)
        .attr("fill", "green")
        .append("g");

      var brushY = d3
        .brushY()
        .extent(function (d, i) {
          return [
            [x(i)   delim / 2, 0],
            [x(i)   x(1) - delim / 2, heightY],
          ];
        })
        .on("brush", brushmoveY)
        .on("end", brushendY);

      var svgbrushY = svgY
        .selectAll(".brush")
        .data(data)
        .enter()
        .append("g")
        .attr("class", "brush")
        .append("g")
        .call(brushY)
        .call(brushY.move, function (d) {
          return [d.value, 0].map(scaleY);
        });
        
       svgbrushY.select(".selection")
         .datum(d=>d)
         .style("fill", function(d) { return color(d.value); })
        
       
       svgbrushY
        .append("text")
        .attr("y", function (d) {
          return scaleY(d.value)   25;
        })
        .attr("x", function (d, i) {
          return x(i)   x(0.5);
        })
        .attr("dx", "-.60em")
        .attr("dy", -5)
        .style("fill", "white")
        .text(function (d) {
          return d3.format(".2")(d.value);
        });

      function brushendY() {
        if (!d3.event.sourceEvent) return;
        if (d3.event.sourceEvent.type === "brush") return;
        if (!d3.event.selection) {
          svgbrushY.call(brushY.move, function (d) {
            return [d.value, 0].map(scaleY);
          });
        }
      }

      function brushmoveY() {
        if (!d3.event.sourceEvent) return;
        if (d3.event.sourceEvent.type === "brush") return;
        if (!d3.event.selection) return;

        var d0 = d3.event.selection.map(scaleY.invert);
        var d = d3.select(this).select(".selection");

        d.datum().value = d0[0];

        update();
      }
      
   

      function update() {
        svgbrushY
          .call(brushY.move, function (d) {
            return [d.value, 0].map(scaleY);
          })
          .selectAll("text")
          .attr("y", function (d) {
            return scaleY(d.value)   25;
          })
          .text(function (d) {
            return d3.format(".2")(d.value);
          });
   
           svgbrushY.selectAll(".selection").style("fill", function(d) { return color(d.value); })          
      }
      
    </script>
  </body>
</html>

(scaled down for snippet view)

  • Related