1

Is there a way to draw on an HTML canvas such that the resulting image is transparent?

I know you can set globalAlpha, but that will only affect how subsequent drawing operations work: setting globalAlpha=.5, then drawing black on red results in a dark red, not a transparent black.

I know you can canvas.fillStyle='rgba(0,0,0,0.5)', but again, that will only affect the drawing operation, not the color that is drawn (see this fiddle). I find this incredibly counter-intuitive.

When I draw in rgba(0, 0, 0, 0.5) at a point where the canvas was previously red, I want the canvas to be gray at that point and transparent. The reason I want this is that I want to upload the image and get a partially transparent image.

Is there any way to do that?

Johannes Bauer
  • 402
  • 4
  • 15
  • Why can't you simply reset `globalAlpha` after using it? – Chloe Anderson Apr 06 '21 at 14:23
  • @ChloeAnderson : the problem is that it affects *how things are drawn*, not *what is drawn*: setting `globalAlpha`, then drawing black on red will give a dark red, not a transparent black. Editing my question to clarify. – Johannes Bauer Apr 06 '21 at 14:38
  • I'm not sure what you want, if I move the circle I see exactly what I expect. a gray circle on white background, darker red when it overlaps red. https://jsfiddle.net/87vqpr4w/ Can you make an image of what you want to achieve? If you want a grey image(half transparent black is gray on white background) on the red circle you'll have to render a pure white circle first. https://jsfiddle.net/tzu9gev8/ – Tschallacka Apr 06 '21 at 14:47
  • Here it is side by side, is this what you want? That it remains grey as it moves over the red circle? https://jsfiddle.net/edkfpv2r/1/ – Tschallacka Apr 06 '21 at 15:00
  • @Tschallacka thanks, nice fiddle. But no, what I want is the canvas to be gray and half transparent where the gray circle is drawn. I'm struggling to make myself more clear. I've tried editing the question again. Is this clearer now? – Johannes Bauer Apr 06 '21 at 15:03
  • But the circle is gray and half transparent. What do you expect red + 50% black to turn out to? dark red, that's right. As you can see in the top circle of this jsfiddle, it's grey, dark red, dark yellow when it moves. For the second circle I added clipping on the red, where it remains grey due to a white circle being drawn under it https://jsfiddle.net/edkfpv2r/3/ How do you expect your colors to mix when you make it transparent? – Tschallacka Apr 06 '21 at 15:07
  • 1
    I don't want any red to remain where I've drawn transparent black. I want that part of the image to be black... and transparent. If the `div` that the canvas is in has a picture of a kitten as its background, I want that kitten to shine through where I've drawn in transparent black. – Johannes Bauer Apr 06 '21 at 15:12

2 Answers2

0

This is where the compositing operations of CanvasRenderingContext2D might come in handy. If you want to draw a transparent black shape on top of a solid red shape and make the are where both shapes overlap transparent, the 'xor' mode might be the best choice.

Here's an example:

const canvas = document.getElementById('some_canvas');
const ctx = canvas.getContext('2d');
ctx.globalCompositeOperation = 'xor';
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.arc(50, 50, 15, 0, 2 * Math.PI, true);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.arc(60, 50, 10, 0, 2 * Math.PI, true);
ctx.fill();
<canvas id="some_canvas" width="100" height="100"></canvas>
obscure
  • 8,071
  • 2
  • 9
  • 30
  • Thanks, but I'm afraid that isn't what I'm looking for. Sorry for still being unclear, apparently. I want the whole region where I've drawn to have the same color, and the same transparency. I want what's behind the canvas to show through. – Johannes Bauer Apr 06 '21 at 15:14
0

The easiest is probably to draw your semi-transparent part in two passes:

  • first as a cut-out, with an opaque fillStyle,
  • then as an actual paint, with the semi-transparent fillStyle

We can't do a single pass because apart from "clear" all composite modes do take the alpha channel into consideration, so by applying such compositing with a semi-transparent paint, the previous drawing will still be "semi" visible.

const canvas = document.getElementById('some_canvas');
const ctx = canvas.getContext('2d');

ctx.setTransform( 2, 0, 0, 2, -50, -50 );

ctx.beginPath();
ctx.arc(50,50,15,0, 2*Math.PI, true);
ctx.fillStyle='red';
ctx.fill();

ctx.beginPath();
ctx.arc(50,50,10,0, 2*Math.PI, true);
// first pass to clear behind the to-be-painted shape
// notice we keep the fillStyle opaque (the color doesn't matter)
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// second pass, the actual painting, with the desired color
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle='rgba(0,0,0,0.5)';
ctx.fill();
/* CSS checkerboard stolen from https://stackoverflow.com/a/51054396/3702797 */
canvas {
    background-image: /* tint image */
                      linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
                      /* checkered effect */
                      linear-gradient(to right, black 50%, white 50%),
                      linear-gradient(to bottom, black 50%, white 50%);
    background-blend-mode: normal, difference, normal;
    background-size: 2em 2em;
}
<canvas id="some_canvas" width="100" height="100"></canvas>
Kaiido
  • 87,051
  • 7
  • 143
  • 194