Skip to content
Advertisement

How to put overlapping images on html5 canvas?

I want to make a cool visualization where I would overlap lots of semi-transparent images on my canvas. For this, I decided to use context.putImageData() because these images are stored as arrays. The problem is that this method disregards the actual background of the canvas with the already drawn images and computes the transparency with respect to the white background.

Here is a snippet showing the current behavior:

<!DOCTYPE html>
<html>

<body>
<canvas id='cvs' width='100' height='100'></canvas>
</body>
<script>
const size = 50
const context = document.getElementById('cvs').getContext('2d');

const redSquare = context.createImageData(size, size);

for (let i=0; i<size*size*4; i=i+4) {
    redSquare.data[i] = 255;
    redSquare.data[i + 3] = 255; // 1.0 opacity rbga(1, 0, 0, 1)
}

context.putImageData(redSquare, 10, 10);

const trasparentBlackSquare = context.createImageData(size, size);

for (let i=0; i<size*size*4; i=i+4) {
    trasparentBlackSquare.data[i + 3] = 100; // 0.4 opacity rbga(0, 0, 0, 0.4)
}

context.putImageData(trasparentBlackSquare, 30, 30);

</script>

</html>

And the result looks like this: current behavior.

However, I would want it to look this way: expected behavior

I generated this image with two distinct overlapping canvases which is not suitable for my problem.

Please let me know if there are any workarounds to this problem.

Answer

If you write an image into a ‘workspace’ canvas you can go through the image data for that, changing the opacity, just as you have done for the black square. Then write it back to workspace canvas. You can then write it to your main canvas using drawImage.

Here’s what I tried on my server, obviously you have to put in a suitable img src to avoid CORS problems. And also make the img and workspace elements visibility:hidden – I’ve left them visible to show what is going on.

<!DOCTYPE html>
<html>

<body>
img
<img id="img" src="https://rgspaces.org.uk/wp-content/uploads/may-morning-in-lockdown-100x100.jpg"/>
cvs
<canvas id='cvs' width='100' height='100'></canvas>
workspace
<canvas id='workspace' width='100' height='100' style="visibility:visible;"></canvas>
</body>
<script>
window.onload=init;
function init() {
  const size = 50
  const cvs = document.getElementById('cvs')
  const context = cvs.getContext('2d');
  const workspace= document.getElementById('workspace')
  const workspacectx=workspace.getContext('2d');
  const img=document.getElementById('img');

  context.fillStyle = 'rgb(255,0,0,1)';
  context.fillRect(10, 10, size, size);

  workspacectx.drawImage(img,20,20,size,size);

  imgdata=workspacectx.getImageData(20, 20, size, size);
  for (var i=0;i<size*size*4;i+=4) {
    imgdata.data[i+3]=150;//if it already has an opacity 0 you would leave it
  }
  workspacectx.putImageData(imgdata,20,20);
  context.drawImage(workspace,20,20);
}
</script>

</html>
Advertisement