Как я могу обрезать / использовать globalCompositeOperation = "desination-out", чтобы исключить 2 круга из моего холста?

Я пытаюсь выяснить CanvasRenderingContext2D.globalCompositeOperation = "destination-out чтобы создать эффект "Туман войны".

Учитывая этот пример

https://jsfiddle.net/wobzpjLL/

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

// a green background box
ctx.fillStyle = "green";
ctx.fillRect(0,0,300,150); 

// a line
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(300, 150);
ctx.stroke();

// Fog of war
ctx.fillStyle = "black";
ctx.fillRect(0,0,300,150); 

// Here I want two circles that shows the background (green & line)

// text
ctx.fillStyle = "red";
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50); 

Как создать два круга, исключающих черное наложение, чтобы пользователь мог видеть зеленый фон и линию внутри этих двух кругов, черные везде (следовательно, создавая эффект тумана войны)?

1 ответ

Решение

Используя маски

Туман войны с использованием композитинга

Самый простой способ добиться маскировки - создать закадровый холст и отобразить на нем маску. Затем при анимации вы визуализируете мир, а затем применяете маску с "destination-in", Вы можете использовать CSS-фон холста в качестве тумана (как это сделано в демоверсии) или вы можете визуализировать туман поверх с помощью "destination-over" также показано в демо, используя текст.

Слоистый рендеринг

Лично и для производительности (если код нуждается в каждом разрыве, который он может получить), я бы избегал globalCompositeOperation и визуализировать туман над миром, создав инверсию маски в демоверсии. Это добавляет только один дополнительный вызов рендеринга и нет необходимости изменять составные операции

Пример использования композиции

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function createCanvas(){
  var c = document.createElement("canvas");
  c.width = 300;
  c.height = 150;
  c.ctx = c.getContext("2d");
  return c;
}

// draw game world on offscreen canvas
function drawBackground(ctx){
  ctx.fillStyle = "green";
  ctx.fillRect(0,0,300,150); 

  // a line
  ctx.beginPath();
  ctx.lineWidth = 3;
  ctx.strokeStyle = "#08A";
  ctx.lineTo(0, 0);
  ctx.lineTo(300, 150);
  ctx.stroke();
}

// create and render world
var bGround = createCanvas();
drawBackground(bGround.ctx)


// create mask 
var grad = ctx.createRadialGradient(0,0,60,0,0,0);
grad.addColorStop(0,"rgba(0,0,0,0.0)");
grad.addColorStop(0.2,"rgba(0,0,0,1)");
grad.addColorStop(1,"rgba(0,0,0,1)");

var mask = createCanvas();
mask.ctx.setTransform(1,0,0,1,75,75);
mask.ctx.fillStyle = grad;
mask.ctx.beginPath();
mask.ctx.arc(0,0,60,0,Math.PI*2);
mask.ctx.fill();
mask.ctx.setTransform(1,0,0,1,225,75);
mask.ctx.beginPath();
mask.ctx.arc(0,0,60,0,Math.PI*2);
mask.ctx.fill();
document.body.appendChild(mask);

// draws the fog
function drawFog(ctx,x){
  ctx.globalCompositeOperation = "destination-in";
  ctx.drawImage(mask, x, 0);
  ctx.globalCompositeOperation = "source-over";
}

ctx.fillStyle = "red";
ctx.font = "30px Arial";
function loop(time){
  ctx.drawImage(bGround,0,0);
  drawFog(ctx,Math.sin(time / 1000) *20);
  ctx.globalCompositeOperation = "destination-over";
  ctx.fillText("Hello World",10,50); 
  ctx.globalCompositeOperation = "source-over";
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
canvas {
  border:1px solid #d3d3d3;
  
}
<canvas id="myCanvas" width="300" height="150" style="background : black;" ></canvas>
<div>Pre rendered offscreen mask shown below for display only.</div>

Другие вопросы по тегам