Search code examples
javascriptcanvashtml5-canvas

How to Draw a Transparent Image Over the Rest of a Canvas


In this code snippet, I have simplified a version of my problem

What I want:

I have a game (represented by the green background). And on top of that game, I want to draw a slightly transparent image that was drawn and fetched with HTML canvas (represented by the opaque blue square) This image must be drawn as an image, because of it's complexity, and the necessity to load it. This is not shown here, however, to keep the illustration simplistic.

The Issue:

The image is a slightly transparent blue, however the green background isn't showing. I want the background (the game) to show through the image

//Fetch HTML Canvas
var canvas = document.getElementById("context");
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var width = canvas.width;
var height = canvas.height;

//draw a rectangle with a 50% transparency 
//(THIS IMAGE WOULD BE HIDDEN)
ctx.fillStyle = "rgba(47, 0, 255, 0.5)";
ctx.fillRect(10, 10, 100, 100);

//fetch the image
var a = ctx.getImageData(10, 10, 100, 100);

//background
ctx.fillStyle = "green";
ctx.fillRect(0, 0, width, height)

//lay the image down
ctx.putImageData(a, 10, 10);
<canvas id="context"></canvas>

I have already tried capturing the image on a transparent background, but that doesn't seem to work. Every transparent image will not allow the backdrop to show through said image.

I also tried using the ctx.globalAlpha = NUM; command, but that only affects the drawing method. Not the overall image


Solution

  • I'm not sure I understand correctly, but you're drawing the background second, on top of the square. I'd draw the background, then the square, ensuring the opacity is available on the result:

    const canvas = document.getElementById("context");
    const ctx = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    ctx.fillStyle = "green";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    ctx.fillStyle = "rgba(47, 0, 255, 0.5)";
    ctx.fillRect(10, 10, 100, 100);
    
    const imgData = ctx.getImageData(10, 10, 100, 100);
    ctx.putImageData(imgData, 150, 150);
    <canvas id="context"></canvas>

    If you're loading an image that you want to be transparent, and you need to use get/put image data from another canvas, using background from the original canvas, you can try something like:

    const canvas = document.getElementById("context");
    const ctx = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.src = "https://picsum.photos/100/100";
    img.decode().then(() => {
      ctx.fillStyle = "green";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    
      const canvas2 = document.createElement("canvas");
      canvas2.width = canvas2.height = 100;
      const ctx2 = canvas2.getContext("2d");
    
      const imgDataFromCtx = ctx.getImageData(150, 20, 100, 100);
      ctx2.putImageData(imgDataFromCtx, 0, 0);
    
      ctx2.globalAlpha = 0.5;
      ctx2.drawImage(img, 0, 0);
      ctx2.globalAlpha = 1;
    
      const imgData = ctx2.getImageData(0, 0, canvas2.width, canvas2.height);
      ctx.putImageData(imgData, 150, 20);
    });
    <canvas id="context"></canvas>

    That said, I still don't understand the motivation for not simply drawing the image on the original canvas without getting and putting data via a second canvas:

    const canvas = document.getElementById("context");
    const ctx = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    const img = new Image();
    img.src = "https://picsum.photos/100/100";
    img.decode().then(() => {
      ctx.fillStyle = "green";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    
      ctx.fillStyle = "rgba(47, 0, 255, 0.5)";
      ctx.fillRect(10, 10, 100, 100);
    
      ctx.globalAlpha = 0.5;
      ctx.drawImage(img, 100, 20);
      ctx.globalAlpha = 1;
    });
    <canvas id="context"></canvas>

    If this doesn't help, you may have oversimplified your use case and need to provide more context.