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
house
roofOverlay
Here’s how to composite your “roof pattern” on top of your “house” using “roofOverlay”
This is a multi-part process:
- Draw the house on canvas#1.
- Draw the red roofOverlay on canvas#2.
- Set canvas#2’s context.globalCompositeOperation = 'source-in'
- Draw your desired pattern on canvas#2
- 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>