Home > Mobile >  How to set pattern origin at the top left point of each bar of a barchart in d3.js?
How to set pattern origin at the top left point of each bar of a barchart in d3.js?

Time:05-25

I created a barchart and used patterns to fill it. For each pattern, I set x=0 and y=0, but I don't know where this (0,0) point is, so I don't know my pattern start tiling from where .

I want to set the top left corner of bar as the origin point of pattern for each bar. How can I achieve this?

Diagram of the result I want

My code:

<!doctype html>
<html>
<head>
    <style>
    </style>
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>

<svg>
    
    <defs>
        <pattern id="pattern1"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="10" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern2"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="5" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern3"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" >
      
            <circle cx="10" cy="10" r="3" style="stroke: none; fill: #0000ff" />
      
        </pattern>
      </defs>
    
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern3);" />   
</svg>
</body> 
<script>
  var data = [{ year: '2001', value:10 },
            { year: '2002', value:30 },
            { year: '2003', value:20 },
           ]
var svg = d3.select("svg"),
            margin = 200,
            width = svg.attr("width") - margin,
            height = svg.attr("height") - margin


var xScale = d3.scaleBand().range([0, width]).padding(0.4),
            yScale = d3.scaleLinear().range([height, 0]);

var g = svg.append("g")
            .attr("transform", "translate("   100   ","   100   ")");

    

        xScale.domain(data.map(function(d) { return d.year; }));
        yScale.domain([0, d3.max(data, function(d) { return d.value; })]);

        g.append("g")
         .attr("transform", "translate(0,"   height   ")")
         .call(d3.axisBottom(xScale));

        g.append("g")
         .call(d3.axisLeft(yScale).tickFormat(function(d){
             return d;
         }).ticks(10));

        g.selectAll(".bar")
         .data(data)
         .enter().append("rect")
         .attr("class", "bar")
         .attr("x", function(d) { return xScale(d.year); })
         .attr("y", function(d) { return yScale(d.value); })
         .attr("width", xScale.bandwidth())
         .attr("height", function(d) { return height - yScale(d.value); })
         .attr('stroke', "black")
         .attr('stroke-width', '1')
         .attr("fill", function(d,i) { return  "url(#pattern"   (i 1)  ")"});
    

</script>

</html>

Thank you in advance.

CodePudding user response:

What you could do, is to add the patterns using D3 as well. D3 actually lets you add any type of tag to the DOM. Therefore, you could do a second join to add the patterns. In the following code I just "copied" your logic, but now the pattern is in the g element, and the patterns position is at the top left of your bars. Of course you can now easily tune this, I hope it helps!

<!doctype html>
<html>
<head>
    <style>
    </style>
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>

<svg>
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern3);" />   
</svg>
</body> 
<script>
  var data = [{ year: '2001', value:10 },
            { year: '2002', value:30 },
            { year: '2003', value:20 },
           ]
var svg = d3.select("svg"),
            margin = 200,
            width = svg.attr("width") - margin,
            height = svg.attr("height") - margin

var xScale = d3.scaleBand().range([0, width]).padding(0.4),
    yScale = d3.scaleLinear().range([height, 0]);


var g = svg.append("g")
            .attr("transform", "translate("   100   ","   100   ")");


        xScale.domain(data.map(function(d) { return d.year; }));
        yScale.domain([0, d3.max(data, function(d) { return d.value; })]);

g.selectAll("defs.pattern")
         .data(data)
         .enter()
         .append("defs").append("pattern")
              .classed("pattern", true)
              .attr("id", (_, i) => `pattern${i   1}`)
              .attr("x", (d) => xScale(d.year))
              .attr("y", (d) => yScale(d.value))
              .attr("width", 20)
              .attr("height", 20)
              .attr("patternUnits", "userSpaceOnUse")
              .append("circle")
                  .attr("r", (_, i) => 3 * (i   1))
                  .attr("cx", 10)
                  .attr("cy", 10)
                  .style("stroke", null)
                  .style("fill", "#0000ff")

        g.append("g")
         .attr("transform", "translate(0,"   height   ")")
         .call(d3.axisBottom(xScale));

        g.append("g")
         .call(d3.axisLeft(yScale).tickFormat(function(d){
             return d;
         }).ticks(10));

        g.selectAll(".bar")
         .data(data)
         .enter().append("rect")
         .attr("class", "bar")
         .attr("x", function(d) { return xScale(d.year); })
         .attr("y", function(d) { return yScale(d.value); })
         .attr("width", xScale.bandwidth())
         .attr("height", function(d) { return height - yScale(d.value); })
         .attr('stroke', "black")
         .attr('stroke-width', '1')
         .attr("fill", function(d,i) { return  "url(#pattern"   (i 1)  ")"});


    

</script>

</html>

  • Related