How to add transparency values correctly?

71 views Asked by At

The following example draws two boxes. The first box is just one box drawn with a globalAlpha of 1. The second box is the sum of 2 boxes with globalAlpha of 0.5.

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

ctx.fillStyle = "blue";

ctx.globalAlpha = 1.0;

ctx.fillRect(0, 0, 100, 100);

ctx.globalAlpha = 0.5;

ctx.fillRect(100, 0, 100, 100);
ctx.fillRect(100, 0, 100, 100);
<canvas id="canvas"></canvas>

It seems to me that the two alpha values of 0.5 do not sum to 1, because the boxes do not have the same color. This is how it looks on my screen.

boxes

How to bisect an alpha correctly?

1

There are 1 answers

5
ceving On

This seems to work somehow, but I am not sure if I can rely on 2.2.

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

ctx.globalAlpha = 1;

ctx.fillStyle = "#00f";
ctx.fillRect(0, 0, 100, 100);

const gamma = 2.2;
const count = 100;

ctx.globalAlpha = Math.pow(1/count, 1/gamma);

for (let i = 0; i < count; i++)
  ctx.fillRect(100, 0, 100, 100);
canvas {
  background: repeating-conic-gradient(#000 0deg 25%, #fff 0deg 50%) 0 / 2em 2em;
  border: 1px solid black;
}
<canvas id="canvas" width="200" height="100"></canvas>

The error is visible for 100 iterations.

const gamma = 2.2;
const iterations = document.getElementById("iterations");
const color = document.getElementById("color");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rect = canvas.getBoundingClientRect();
const hover = [0, 1, 2, 3].map(id => document.getElementById(id));

function draw() {
  ctx.globalAlpha = 1;
  ctx.clearRect(0, 0, 200, 100);
  ctx.fillStyle = color.value;
  ctx.fillRect(0, 0, 100, 100);
  const count = Number(iterations.value);
  ctx.globalAlpha = Math.pow(1/count, 1/gamma);
  for (let i = 0; i < count; i++)
    ctx.fillRect(100, 0, 100, 100);
}  
draw();

iterations.addEventListener("change", ev => draw());
color.addEventListener("change", ev => draw());
canvas.addEventListener("mousemove", ev => {
  const pixel = ctx.getImageData(ev.clientX - rect.left, ev.clientY - rect.top, 1, 1);
  pixel.data.forEach((v, i) => {hover[i].value = v;});
});
canvas {
  background: repeating-conic-gradient(#000 0deg 25%, #fff 0deg 50%) 0 / 2em 2em;
  border: 1px solid black;
}
<p>Iterations: <input type="number" id="iterations" value="1000"/></p>
<p>Color: <input type="color" id="color" value="#ffffff"/></p>
<canvas id="canvas" width="200" height="100"></canvas>
<div>R: <output id="0"></output></div>
<div>G: <output id="1"></output></div>
<div>B: <output id="2"></output></div>
<div>A: <output id="3"></output></div>