HTML5 Canvas Global Composite Operations Tutorial and Demo

Updated on September 1, 2017

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)

HTML5 composite operations
Canvas Composite Operations

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

Was this article helpful?

Related Articles

Leave a Comment