Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In HTML5 canvas, how to mask an image with a background of my choice?

I tried to make this happen with canvas' globalCompositeOperation, but had no luck so I'm asking here. There are similar questions here, but I did not find my case among them.

I have layers in my canvas area as so (drawing order from bottom to top):

  • The canvas base is filled with pure white (#fff, with fillRect)
  • First image house is a picture of a house. The background is transparent. (see below)
  • Second image roofOverlay is an overlay "masking" image that has the roof area coloured red (can be anything, but red for clarity, see below)

Both images take up the whole canvas and are lined up perfectly on top of each other, so that the red roof area matches the house.

I then have a repeating background repeatPattern pattern what I want to use ONLY inside the red areas: to fill the red area with repeatPattern. (can be anything, but assume hexagons or whatever)

In pseudocode, this would ideally be something in the lines of:

roofOverlay.maskBackground(repeatPattern)

(On a sidenote, I would also like to be able to mess with the background pattern HSL-values, but I think that's quite straightforward once I get the pattern to even display)

Expected result:

The expected result would be a house which roof is textured with the repeatPattern image.

Note: I'm aware of clipping paths with masks, but I cannot use them here. The example is simplified and drawing all the paths for multiple different houses would be way too much work. I only have the overlayed png-files for the roof.

Images for reference

Househouse


House roof overlayroofOverlay

like image 588
Harri Virtanen Avatar asked Mar 20 '13 14:03

Harri Virtanen


People also ask

Can canvas have a background image?

As well as adding elements such as images, icons, and text, you have the option to personalize the background of your canvas. Applying a different background can completely change the appearance of your creation.


1 Answers

Here’s how to composite your “roof pattern” on top of your “house” using “roofOverlay”

enter image description here

This is a multi-part process:

  1. Draw the house on canvas#1.
  2. Draw the red roofOverlay on canvas#2.
  3. Set canvas#2’s context.globalCompositeOperation = 'source-in'
  4. Draw your desired pattern on canvas#2
  5. Compositing will cause your desired pattern to replace the red overlay—only in the red overlay area.

Here is a Fiddle that loads grass on your roof: http://jsfiddle.net/m1erickson/SWP6v/

And here is code that uses a lattice pattern fill on your roof:

Note: I'm assuming that you want the house and roof on separate canvases so you can flip through a variety of roof choices. If you need everything on 1 canvas, you can just draw the roof canvas onto the house canvas.

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
    #container{position:relative;}
    #house{position:absolute; top:0; left:0;}
    #canvas{position:absolute; top:0; left:0;}
</style>

<script>
$(function(){

    var house=document.getElementById("house");
    var ctxHouse=house.getContext("2d");
    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var red=new Image();
    red.onload=function(){
       canvas.width=red.width;
       canvas.height=red.height;

       var houseImage=new Image();
       houseImage.onload=function(){
           house.width=houseImage.width;
           house.height=houseImage.height;
           ctxHouse.drawImage(houseImage,0,0);
       }
       houseImage.src="https://dl.dropbox.com/u/1122582/stackoverflow/house.png";

       ctx.drawImage(red,0,0);

        var imageObj = new Image();
        imageObj.onload = function() {
          var pattern = ctx.createPattern(imageObj, 'repeat');
          ctx.globalCompositeOperation = 'source-in';
          ctx.rect(0, 0, canvas.width, canvas.height);
          ctx.fillStyle = pattern;
          ctx.fill();
        };
        imageObj.src = "http://dl.dropbox.com/u/139992952/stackoverflow/lattice.jpg";
    }
    red.src="https://dl.dropbox.com/u/1122582/stackoverflow/roof-overlay.png";

}); // end $(function(){});
</script>

</head>

<body>
<div id="container">
        <canvas id="house" width=300 height=300></canvas>
        <canvas id="canvas" width=300 height=300></canvas>
</div>
</body>
</html>
like image 173
markE Avatar answered Oct 13 '22 17:10

markE