HTML5 Canvas - 无法在掩码上应用source-atop

问题描述 投票:0回答:1

对不起,我是Canvas的新手,不知道如何谷歌这个。问题是如果前一层(夜空)存在,我无法在掩模上绘制。

以下是两个片段:

const canvas = document.querySelector('#board canvas');
    const ctx = canvas.getContext('2d');
    const { width: w, height: h } = canvas;

    // first layer

    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, w, h);
    ctx.fillStyle = '#555';

    let x, y, radius;
    for (let i = 0; i < 550; i++) {
      x = Math.random() * w;
      y = Math.random() * h;
      radius = Math.random() * 3;
      ctx.beginPath();
      ctx.arc(x, y, radius, 0, Math.PI * 2, false);
      ctx.fill();
    }
    
    // destination

    ctx.font = 'bold 70pt monospace';
    ctx.fillStyle = 'black';
    ctx.fillText('FOO', 10, 60);
    ctx.fillText('BAR', 10, 118);
    ctx.fill();
    
    // source 

    ctx.globalCompositeOperation = 'source-atop';

    for (let i = 0; i < 6; i++) {
      ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
      ctx.fillRect(0, i * 20, 200, 20);
    }
<div id="board">
  <canvas width="640" height="480"></canvas>

</div>

预期结果(但第一层 - 夜空):

const canvas = document.querySelector('#board canvas');
    const ctx = canvas.getContext('2d');
    const { width: w, height: h } = canvas;
    
    // destination

    ctx.font = 'bold 70pt monospace';
    ctx.fillStyle = 'black';
    ctx.fillText('FOO', 10, 60);
    ctx.fillText('BAR', 10, 118);
    ctx.fill();
    
    // source 

    ctx.globalCompositeOperation = 'source-atop';

    for (let i = 0; i < 6; i++) {
      ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
      ctx.fillRect(0, i * 20, 200, 20);
    }
<div id="board">
  <canvas width="640" height="480"></canvas>
</div>
html5 html5-canvas
1个回答
2
投票
  • 合成将影响整个上下文。
  • source-atop模式只会在存在像素的地方绘制(即只有alpha> 0的地方)。
  • 绘制背景时,上下文的所有像素都将Alpha值设置为1。

这意味着source-atop不会在完全不透明的图像上产生任何内容。

一旦你理解了这些要点,很明显你需要单独进行合成。 例如,可以在不同的离屏画布上使用ctx.drawImage(canvas, x, y)在主画布上绘制。

const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
  width: w,
  height: h
} = canvas;

// background
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#555';

let x, y, radius;
for (let i = 0; i < 550; i++) {
  x = Math.random() * w;
  y = Math.random() * h;
  radius = Math.random() * 3;
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI * 2, false);
  ctx.fill();
}


// text compositing on an off-screen context
const ctx2 = Object.assign(document.createElement('canvas'), {
    width: 200,
    height: 120
  }).getContext('2d');
// text
ctx2.font = 'bold 70pt monospace';
ctx2.fillStyle = 'black';
ctx2.fillText('FOO', 10, 60);
ctx2.fillText('BAR', 10, 118);

ctx2.globalCompositeOperation = 'source-atop';
// rainbow
for (let i = 0; i < 6; i++) {
  ctx2.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
  ctx2.fillRect(0, i * 20, 200, 20);
}
// now draw our off-screen canvas on the main one
ctx.drawImage(ctx2.canvas, 0, 0);
<div id="board">
  <canvas width="640" height="480"></canvas>

</div>

或者,因为这是你作文中唯一的合成,你也可以在同一个场景中完成所有这些,但使用其他合成模式:destination-over。 此模式将隐藏现有内容,这意味着您必须在进行合成后实际绘制背景。

const canvas = document.querySelector('#board canvas');
const ctx = canvas.getContext('2d');
const {
  width: w,
  height: h
} = canvas;
//
// text compositing on a clear context
drawText();
// will draw only where the text has been drawn
ctx.globalCompositeOperation = 'source-atop';
drawRainbow();
// from here we will draw behind
ctx.globalCompositeOperation = 'destination-over';
// so we need to first draw the stars, otherwise they'll be behind
drawStars();
//And finally the sky black background
drawSky();

//... reset
ctx.globalCompositeOperation = 'source-over';

function drawSky() {
  ctx.fillStyle = 'black';
  ctx.fillRect(0, 0, w, h);
}

function drawStars() {
  ctx.fillStyle = '#555';
  let x, y, radius;
  for (let i = 0; i < 550; i++) {
    x = Math.random() * w;
    y = Math.random() * h;
    radius = Math.random() * 3;
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, false);
    ctx.fill();
  }
}

function drawText()  {
  ctx.font = 'bold 70pt monospace';
  ctx.fillStyle = 'black';
  ctx.fillText('FOO', 10, 60);
  ctx.fillText('BAR', 10, 118);
}

function drawRainbow() {
  for (let i = 0; i < 6; i++) {
    ctx.fillStyle = `hsl(${i * (250 / 6)}, 90%, 55%)`;
    ctx.fillRect(0, i * 20, 200, 20);
  }
}
<div id="board">
  <canvas width="640" height="480"></canvas>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.