7
votes

I am trying to apply a pattern to a D3 bar chart, but what I get is this:

enter image description here

  • the chart should stop exactly at 100,000
  • the pattern should be "fluid"

I am using a green and red pattern defined as follows:

  var defs = this.svg.append("g:defs");
  defs.append("g:pattern")
    .attr("id", "red-fill")
    .attr("patternUnits", "userSpaceOnUse")
    .attr("width", "85")
    .attr("height", "10")
    .append("g:image")
    .attr("xlink:href", "../10px-barchart-red.png")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 85)
    .attr("height", 10);

  var defs = this.svg.append("g:defs");
  defs.append("g:pattern")
    .attr("id", "green-fill")
    .attr("patternUnits", "userSpaceOnUse")
    .attr("width", "85")
    .attr("height", "10")
    .append("g:image")
    .attr("xlink:href", "../10px-barchart-green.png")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 85)
    .attr("height", 10);

And the plot is made with:

  this.svg.selectAll("rect")
    .data(dataset, getKeys)
    .enter()
    .append("rect")
    .attr('class', 'bar')
    .attr("x", function(d, i) {
        return x(i) + 44;
    })
    .attr("y", function(d, i) {
        return y(d.value);
    })
    .attr("width", x.rangeBand())
    .attr("height", function(d, i) {
        return height + padding - y(d.value);
    })
    .attr("fill", function(d) {
      if (d.key == 0) {
        return "url(#green-fill)";
      } else {
        return "url(#red-fill)";
      }
    })
1
It looks like there might be something wrong with the scale you're using for "y". Is the scale outputting the expected position for d.value?Scott Cameron
For the pattern you need to make sure that the bar is matching the dimensions of the image or the image is tileable. Nothing really D3 can do about this.Lars Kotthoff

1 Answers

3
votes

This block by John Schulz successfully makes patterned bar charts that look fantastic (https://bl.ocks.org/jfsiii/7772281).

Copied the code in the below snippet for convenience and perpetuity (also added a little animation to show that it transitions well also).

var first = true;

setInterval( function(){
  
  if(first){
    d3.select('.thing-2').transition()
      .delay(500)
      .duration(1000)
      .attr('height',20)
      .attr('y',80)
  }else{
    d3.select('.thing-2').transition()
      .delay(500)
      .duration(1000)
      .attr('height',100)
      .attr('y',0)
  }
  
  first = !first;

  },2500)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<html>
  <head>
    <meta charset=utf-8 />
    <title>SVG colored patterns via mask</title>
    <style>
      /* FF seems to need explicit dimensions */
      svg {
        width: 500px;
        height: 500px;
      }

      rect.hbar {
        mask: url(#mask-stripe)
      }

      .thing-1 {
        fill: blue;
      }


      .thing-2 {
        fill: green;
      }
    </style>
  </head>
  <body>
    <svg>
      <defs>
        <pattern id="pattern-stripe" 
          width="4" height="4" 
          patternUnits="userSpaceOnUse"
          patternTransform="rotate(45)">
          <rect width="2" height="4" transform="translate(0,0)" fill="white"></rect>
        </pattern>
        <mask id="mask-stripe">
          <rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
        </mask>      
      </defs>

      <!-- bar chart -->
      <rect class="hbar thing-2" x="0" y="0" width="50" height="100"></rect>
      <rect class="hbar thing-2" x="51" y="50" width="50" height="50"></rect>
      <rect class="hbar thing-2" x="102" y="25" width="50" height="75"></rect>
      
      <!-- horizontal bar chart -->
      <rect class="hbar thing-1" x="0" y="200" width="10" height="50"></rect>
      <rect class="hbar thing-1" x="0" y="251" width="123" height="50"></rect>
      <rect class="hbar thing-1" x="0" y="302" width="41" height="50"></rect>
      
    </svg>
  </body>
</html>