littlered
littlered

Reputation: 300

Why is my canvas fill pattern repeating/overlapping itself?

I've drawn a grid of rounded rectangles, and I need to fill them with an image background. Eventually I'll have many image backgrounds, but for now, I'm trying to get it to work with one. I'm close, my rectangles draw but the fill does something a little whacky - it overlaps itself and kills my pattern (except on the edges) basically filling the whole canvas with the image. I've tried to 'clip' my path but that just causes only one rectangle to fill. I'm not sure what I'm doing wrong, and I'm hoping a canvas expert can spot it?

/* build rounded rectangle */
var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke) 
{ 

 ctx.save(); // save the context so we don't mess up others ctx.beginPath(); 

// draw top and top right corner ctx.moveTo(x+radius,y);
ctx.arcTo(x+width,y,x+width,y+radius,radius); 

// draw right side and bottom right corner 
ctx.arcTo(x+width,y+height,x+width-radius,y+height,radius); 

// draw bottom and bottom left corner 
ctx.arcTo(x,y+height,x,y+height-radius,radius); 

// draw left and top left corner 
ctx.arcTo(x,y,x+radius,y,radius); 
ctx.clip(); 

if(fill){ ctx.fill(); } 

if(stroke){ ctx.stroke(); } 

 ctx.restore(); // restore context to what it was on entry 
} 

/* onload, fill canvas with pattern of rounded rectangles separated by 2px */
window.onload = function() {
var canvas = document.getElementById("canvas");
/* rounded filled rectangle pattern */
var canvasWidth=530;
    var canvasHeight=530;
    var recWidth=42;
    var recHeight=42;
    var grout=2;
    var radius=2;
    var cols=canvasWidth/(recWidth+grout);
    var rows=canvasWidth/(recHeight+grout);


    /* loop through each row/column to build pattern */
    alert("rows" + rows + " & cols " + cols);
    for (i=1; i<rows; i++){
        for (j=1; j<cols; j++){
            var ctx = canvas.getContext("2d");
            /* fill pattern */
            var img=document.getElementById("poppy");
            var pat=ctx.createPattern(img,"repeat");
            ctx.fillStyle=pat;  
            roundedRect(ctx,(j*grout + (j-1)*recWidth),(i*grout + (i-1)*recHeight),recWidth,recHeight,radius,true,false);       

                }
             }
};

Upvotes: 3

Views: 1101

Answers (1)

Phrogz
Phrogz

Reputation: 303168

Let's assume that your problem is reproduced in this JSFiddle: http://jsfiddle.net/hBEwr/. (If this is not the case, the rest of this answer is ~bunk; please edit to match your exact problem.)

  1. If we remove the pattern from the equation altogether, we can see that it is not directly to blame.
    http://jsfiddle.net/hBEwr/1/
    Instead, the rounded rects are not leaving the grout necessary between them.

  2. Since everything is running over each other, we then change the fill color to a low-opacity blue. Interesting!
    http://jsfiddle.net/hBEwr/2/
    We see that many of the paths are getting drawn again and again. This insight leads us to look at the implementation of roundedRect() more closely and notice that we never call beginPath(). Indeed, in your code from the question above it seems to have gotten eaten by a comment. Without this call, each invocation of roundedRect() adds additional rectangles to an ever-growing path, instead of starting over.

  3. Adding the call to beginPath() back in, we see that we are on the path to success:
    http://jsfiddle.net/hBEwr/3/

  4. Now we can add back in the pattern (with some slight changes) and achieve glorious victory:
    http://jsfiddle.net/hBEwr/4/
    Poppies! Poppies! Poppies!

Some notes on other small tweaks I made:

  • Make sure you always var your local variables:

    for (i=0; i<10; i++)     // Oops! i is a global variable
    for (var i=0; i<10; i++) // Much better
    
  • It's inefficient to re-get the ctx, img, and create the pattern for each tile. In the final code above I've moved these outside the loops for simplicity and speed.

  • Using console.log instead of alert for debugging is often far easier. (Open the developer console to see the output.)

  • I added the final outer row/col of grout the the column calculations to ensure that there is space for it to be included. Use or not as you like.

Upvotes: 2

Related Questions