asdjfiasd
asdjfiasd

Reputation: 1730

Is it possible to achieve html canvas composite operation "lighter" if I have 2 canvases on top of each other?

I want to display white to transparent gradient on top of a green canvas. I don't want the gradient to fade to dark so I use "lighter" composite operation. This works perfectly. First gradient is what I want. It goes from white to background gradually, second gradient uses source-over and it goes from white to dark. That's not what I want.

var ac = a.getContext('2d');
// green background
ac.fillStyle = 'green';
ac.fillRect(0, 0, 100, 100);
// white-to-transparent gradient but lighter CO
var g = ac.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, 'transparent');
ac.fillStyle = g;
ac.globalCompositeOperation = 'lighter';
ac.fillRect(20, 20, 60, 10);
// white-to-transparent gradient
ac.globalCompositeOperation = 'source-over';
ac.fillRect(20, 40, 60, 10);
<canvas id="a" width=100 height=100></canvas>

Because in real code I use complicated compositing and for performance reasons I decided to split this into two canvases. Background canvas is green and gradients are shown on top. The problem is that "lighter" now doesn't work. Is it possible to achieve the same result as in previous case?

var bc = b1.getContext('2d');
// green background
bc.fillStyle = 'green';
bc.fillRect(0, 0, 100, 100);

var bc = b2.getContext('2d');
// white-to-transparent gradient but lighter CO
var g = bc.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, 'transparent');
bc.fillStyle = g;
bc.globalCompositeOperation = 'lighter';
bc.fillRect(20, 20, 60, 10);
// white-to-transparent gradient
bc.globalCompositeOperation = 'source-over';
bc.fillRect(20, 40, 60, 10);
// white-to-#ffffff00 gradient source-over
var g = bc.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, '#ffffff00');
bc.globalCompositeOperation = 'source-over';
bc.fillRect(20, 60, 60, 10);
// white-to-#00000000 gradient source-over
var g = bc.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, '#00000000');
bc.globalCompositeOperation = 'source-over';
bc.fillRect(20, 80, 60, 10);
<div>
  <canvas id="b1" width=100 height=100 style="position: fixed; left: 0; top: 0;"></canvas>  
  <canvas id="b2" width=100 height=100 style="position: fixed; left: 5px; top: 5px; border: 1px solid red;"></canvas>
</div>

As you can see none of the combinations achieve the same result. I cannot use white-to-green gradient because in real code background is not just green. In these demos I use green to demostrate it. So is that even possible? How to achieve same result?

Upvotes: 0

Views: 254

Answers (1)

Gabriele Petrioli
Gabriele Petrioli

Reputation: 195972

The canvas composite operations only work with the source canvas and only at the time of drawing. If you want to apply effects between two canvas elements you will need to resort to normal CSS like mix-blend-mode (which is not the same though)


If you want to avoid having to re-draw the whole background you could have a separate (off-screen maybe) canvas, and copy that to you final image with drawImage

var bc1 = b1.getContext('2d');
// green background
bc1.fillStyle = 'green';
bc1.fillRect(0, 0, 100, 100);

var bc2 = b2.getContext('2d');
bc2.drawImage(b1, 0, 0);
// white-to-transparent gradient but lighter CO
var g = bc2.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, 'transparent');
bc2.fillStyle = g;
bc2.globalCompositeOperation = 'lighter';
bc2.fillRect(20, 20, 60, 10);
// white-to-transparent gradient
bc2.globalCompositeOperation = 'source-over';
bc2.fillRect(20, 40, 60, 10);
// white-to-#ffffff00 gradient source-over
var g = bc2.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, '#ffffff00');
bc2.globalCompositeOperation = 'source-over';
bc2.fillRect(20, 60, 60, 10);
// white-to-#00000000 gradient source-over
var g = bc2.createLinearGradient(20, 0, 80, 0);
g.addColorStop(0, 'white');
g.addColorStop(1, '#00000000');
bc2.globalCompositeOperation = 'source-over';
bc2.fillRect(20, 80, 60, 10);
#b1 {
  display: none
}

#b2 {
  position: fixed;
  left: 5px;
  top: 5px;
  border: 1px solid red;
}
<div>
  <canvas id="b1" width="100" height="100"></canvas>
  <canvas id="b2" width="100" height="100"></canvas>
</div>

Upvotes: 1

Related Questions