I came across an interesting HTML5 feature called “Canvas composite Operations”. To explain this, lets consider a canvas element with a shape drawn on it. What happens when you draw another shape over whatever has already been drawn? Obviously the newly drawn things will appear over the existing ones isn’t? Composite operations will allow you to change how the new shape or content is drawn on an Canvas element. It means, you can draw a new content on top of the existing content or behind the existing content or even perform interesting Boolean operation like XOR. Well, it doesn’t stop there, HTML5 provides twelve composite operations.
The most important thing is, you can only perform one composite operation on a canvas. In case, if you want to use multiple composite operations, then you will have to create a hidden canvas and then copy the rendered results to the visible canvas.
To keep our demonstration easily understandable, we’ll use two terms; Destination Image and Source Image. The Destination Image is the one that already exists on the canvas and the Source Image is the one that will be drawn over the destination. The operations are achieved using globalCompositeOperation; which will set or return how a source (new image) is drawn onto a destination (existing image).
var context = document.getElementById('compositeCanvas').getContext('2d'); context.globalCompositeOperation = 'source-over';
The default value of globalCompositeOperation is ‘source-over’, which is one among the twelve operations supported at the time of writing this article.
Lets see all the properties; what it means and demo as well (scroll down and click “Live Demo” button)
source-over
This is the default value of globalCompositeOperation property. It draws the source image over the destination image.
var c=document.getElementById('sourceOver'); var context=c.getContext('2d'); context.fillStyle='blue'; context.fillRect(15,15,70,70); context.globalCompositeOperation='source-over'; context.beginPath(); context.fillStyle='red'; context.arc(75,75,35,0,Math.PI*2,true); context.fill();
source-atop
source-atop draws the source image on top of the destination image, but the part of the source image that is outside the destination image is not shown.
context.globalCompositeOperation='source-atop';
source-in
source-in draws the source image into the destination image. Only the part of the source image that is INSIDE the destination image will be visible and the destination image turns transparent.
context.globalCompositeOperation='source-in';
source-out
source-out draws the source image out of the destination image. Only the part of the source image that is OUTSIDE the destination image is visible and the destination image is transparent.
context.globalCompositeOperation='source-out';
destination-over
destination-over draws the destination image over the source image.
context.globalCompositeOperation='destination-over';
destination-atop
destination-atop draws destination image on top of the source image. Part of the destination image that is OUTSIDE the source image is not visible.
context.globalCompositeOperation='destination-atop';
destination-in
destination-in draws the destination image into the source image. The part of the destination image that is INSIDE the source image is visible and the source image turns transparent.
context.globalCompositeOperation='destination-in';
destination-out
destination-out draws the destination image out of the source image. The part of the destination image that is OUTSIDE the source image is shown and the source image becomes transparent.
context.globalCompositeOperation='destination-out';
lighter
lighter displays the source image + the destination image
context.globalCompositeOperation='lighter';
copy
Only the source image is drawn and the destination image is ignored.
context.globalCompositeOperation='copy';
xor
Whatever is drawn is the output of source image XOR destination image.
context.globalCompositeOperation='xor';
darker
context.globalCompositeOperation='darker';
One Composite operation on a Canvas
As I told earlier, you can only do one composite operation on a canvas. If you wish to draw more than one composite operation, then you need a hidden canvas, perform composite operation, copy the rendered outputs to the visible canvas.
How to draw multiple composite Operations in a Canvas?
Step 1: Create two canvas; one is the visible and the other is hidden. We’ll perform operations on hidden canvas and copy the output to the visible canvas one at a time. Means, perform operation on a hidden canvas and copy it to visible canvas…now another operation on a hidden canvas and copy it to visible canvas and so on…
<canvas id="visibleCanvas" width="630" height="500"></canvas> <canvas id="hiddenCanvas" width="630" height="500" style="display:none;"></canvas>
The script starts here..
var canvas = document.getElementById('visibleCanvas'); var context = canvas.getContext('2d'); var hiddenCanvas = document.getElementById('hiddenCanvas'); var hiddenContext = hiddenCanvas.getContext('2d'); //Define dimensions var sqWidth = 65; var arcRad = 45; var shapeOffset = 60; var operationOffset = 160;
Step 2: Lets create an array to store all the operations and loop through it.
var operations = []; operations.push('source-atop'); operations.push('source-in'); operations.push('source-out'); operations.push('source-over'); operations.push('destination-atop'); operations.push('destination-in'); operations.push('destination-out'); operations.push('destination-over'); operations.push('lighter'); operations.push('darker'); operations.push('xor'); operations.push('copy');
Step 3: Loop through the operations
for(var n = 0; n < operations.length; n++) { ........................................}
Inside the loop,
1. Save the hidden canvas first, so that it can be restored later.
hiddenContext.save();
2. Before drawing anything on hiddenCanvas, lets clear previously drawn image.
hiddenContext.clearRect(0, 0, canvas.width, canvas.height);
3. Draw the destination image; the rectangle.
hiddenContext.beginPath(); hiddenContext.rect(0, 0, sqWidth, sqWidth); hiddenContext.fillStyle = '#e37026'; hiddenContext.fill();
4. Set the composite operation.
hiddenContext.globalCompositeOperation = thisOperation;
5. Draw the source image; the circle.
hiddenContext.beginPath(); hiddenContext.arc(shapeOffset, shapeOffset, arcRad, 0, 2 * Math.PI, false); hiddenContext.fillStyle = '#0a518f'; hiddenContext.fill(); hiddenContext.restore();
6. Translate the visible canvas, so that the images are drawn at right position.
if(n > 0) { if(n % 4 === 0) { context.translate(operationOffset * -3, operationOffset); } else { context.translate(operationOffset, 0); } }
7. Copy the output of hiddenCanvas to visibleCanvas
context.drawImage(hiddenCanvas, 0, 0);
Checkout the full source code and demo by clicking the “Live Demo” button.
Credits:
Of course, I referred : W3Schools.org