【问题标题】:Overwrite drawn over canvas when drawing transparent shapes绘制透明形状时覆盖画布上的绘制
【发布时间】:2016-08-30 22:07:01
【问题描述】:

我看不到这已经发布了,所以就这样吧。

假设我在画布上画了 2 个正方形。

var c = document.getElementById('test'), ctx = c.getContext('2d');
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);
ctx.fill();

这会产生这个图像:

如果我将 globalAlpha 更改为 0.5,我会得到:

但是,我想制作这个:

就像这样,所有像素都是透明的,它下面的任何图像都会出现,但红色三角形创建的像素会覆盖它所绘制的现有蓝色三角形。

并且 ctx.globalComposisteOperation 在这种情况下似乎没有帮助,因为它还考虑了透明度以及我想保留两个正方形的事实。

有没有办法用当前的方法做到这一点?

【问题讨论】:

  • 我不明白。最终结果如何透明?你想要一个白色的透明覆盖层吗?看起来您可以通过简单地更改颜色来获得最终结果,然后您甚至不需要使用 alpha。
  • @Dom 他们可能正在渲染到第二个画布以创建透明覆盖。有点像 UI 层。但我不知道这是否是 OP 实际在做的事情。
  • @MikeC 非常正确,鉴于提供的示例,很难说。

标签: javascript html canvas transparency


【解决方案1】:

在绘制之前使用合成清除红色三角形。

使用合成略好于剪辑,因为您不必清除剪辑。清除剪辑需要保存整个上下文状态,然后恢复该上下文状态——涉及许多属性。合成只需要来回更改 1 个属性。

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

// fill the blue rect
ctx.fillStyle = "rgba(0,0,255,0.5)";
ctx.beginPath();
ctx.moveTo(25, 0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.fill();

// define the red rect path
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(75, 50);
ctx.lineTo(25, 50);
ctx.lineTo(50, 0);

// clear the red rect path using compositing
ctx.globalCompositeOperation='destination-out';
ctx.fillStyle='black';
ctx.fill();
ctx.globalCompositeOperation='source-over';

// fill the red rect
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.fill();
body{ background-color:white; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>

【讨论】:

    【解决方案2】:

    图层

    以 Photoshop 的方式进行操作并创建图层来为您完成工作。创建图层(第二个画布)并不比加载图像更麻烦。您可以创建几十个并且没有问题,它使这种类型的工作变得容易。

    首先创建第二个画布(层)

    // canvas is original canvas 
    var layer = document.createElement("canvas");
    layer.width = canvas.width;  // same size as original 
    layer.height = canvas.height;
    var ctx1 = layer.getContext("2d");
    

    然后在第二个画布上用 alpha = 1 绘制三角形;

    var tri = (x,c)=>{
        ctx1.fillStyle = c;
        ctx1.beginPath();
        ctx1.moveTo(25 + x, 0);
        ctx1.lineTo(50 + x, 50);
        ctx1.lineTo(0 + x, 50);
        ctx1.closePath();
        ctx1.fill();
    }
    tri(0,"#00f");
    tri(25,"#f00");
    

    然后,只需使用所需的 alpha 值在您正在处理的画布上绘制该层。

    ctx.globalAlpha = 0.5;
    ctx.drawImage(layer,0,0);
    

    如果您不需要额外的图层,请通过取消引用来删除画布和上下文。

    ctx1 = undefined;
    layer = undefined; 
    

    或者你可以保留这个图层,然后为背景制作另一个图层并实时混合它们以获得恰到好处的效果

        //
        var canvas = document.createElement("canvas");
        document.body.appendChild(canvas);
        var ctx = canvas.getContext("2d");
    
        var layer = document.createElement("canvas");
        layer.width = canvas.width;  // same size as original 
        layer.height = canvas.height;
        var ctx1 = layer.getContext("2d");
        var tri = (x,c)=>{
            ctx1.fillStyle = c;
            ctx1.beginPath();
            ctx1.moveTo(25 + x, 0);
            ctx1.lineTo(50 + x, 50);
            ctx1.lineTo(0 + x, 50);
            ctx1.fill();
        }
        tri(0,"#00f");
        tri(25,"#0f0");
        tri(50,"#f00");
        ctx.globalAlpha = 0.5;
        ctx.drawImage(layer,0,0);
        layer = ctx1 = undefined;

    【讨论】:

    • 您可以通过调用layer = canvas.cloneNode() 保存两行。另外,我猜 OP 已经在玩图层了(我想他说他会在其他形状上绘制它),但是为此再创建一个图层在某些时候可能会很麻烦,所以 gCO 非常适合更新单个层。
    • @Kaiido gCO 在您覆盖现有内容时会变得很痛苦,我认为这是 OP 想要的,并且可能存在您想要 "screen""color-burn" 的情况。 .. 结果。
    • 我的意思是使用 gCO 更新每个图层,然后根据需要在主图层上绘制它。无论如何,我想 OP 现在有足够的解决方案,每个都有优点和缺点,而且所有这些都取决于我们对成为好法官的了解不够;-)
    【解决方案3】:

    您可以在绘制第二个矩形之前清除它的区域。

    var c = document.getElementById('game'), 
        ctx = c.getContext('2d');
    ctx.globalAlpha = 0.5;
    ctx.fillStyle = "blue";
    ctx.fillRect(0, 0, 50, 50);
    
    // Clear the extra bit of the blue rectangle
    ctx.clearRect(25, 25, 25, 25);
    
    ctx.fillStyle = "red";
    ctx.fillRect(25, 25, 50, 50);
    <canvas id="game" width="320" height="240"></canvas>

    【讨论】:

    • 对不起,应该让我的问题更清楚。我在看到这个后编辑了我的示例,以更好地反映我实际想要做的事情......
    • @Iblob 我看到您在上次编辑时将其更改为三角形。同样的原则仍然适用。清除首先要绘制的区域。如果您实际上并不需要透明度,那么请使用 Dom 的答案。如果您需要在现有图形上绘制它们而不破坏它们,那么您需要指定。
    • 谢谢!我发现了 ctx.clip() 限制绘制到当前路径,然后我可以简单地清除剪辑区域并绘制形状。
    【解决方案4】:

    老实说,我建议只更改颜色并避免使用 alpha。

    var c = document.getElementById('game'), 
        ctx = c.getContext('2d');
    ctx.fillStyle = "#8080FF";
    ctx.fillRect(0, 0, 50, 50);
    ctx.fillStyle = "#ff8080";
    ctx.fillRect(25, 25, 50, 50);
    <canvas id="game" width="320" height="240"></canvas>

    【讨论】:

      【解决方案5】:
      var c = document.getElementById('test'), ctx = c.getContext('2d');
      ctx.save();
      ctx.fillStyle = "rgba(0,0,255,0.5)";
      ctx.beginPath();
      ctx.moveTo(25, 0);
      ctx.lineTo(50, 50);
      ctx.lineTo(0, 50);
      ctx.lineTo(25, 0);
      ctx.fill();
      ctx.fillStyle = "rgba(255,0,0,0.5)";
      ctx.beginPath();
      ctx.moveTo(50, 0);
      ctx.lineTo(75, 50);
      ctx.lineTo(25, 50);
      ctx.lineTo(50, 0);
      ctx.clip();
      ctx.clearRect(0, 0, 100, 100);
      ctx.fill();
      ctx.restore();
      

      只需使用 clip() 获取当前路径,使用 clearRect 清除其中的所有内容,然后正常绘制路径。

      【讨论】:

        【解决方案6】:

        @markE 是对的,使用合成而不是剪辑(真的很重)。

        但是,他的解决方案只有在您使用 rgba 颜色绘制所有内容时才有效。也许我读错了您的问题,但是如果您要从不透明的形状开始并希望使其透明,那么您应该使用copy gCO 和globalAlpha 属性。

        这对性能的影响较小,因为drawImagefill 快​​,并且允许您执行淡出效果;但这真的取决于您的需求。

        var ctx = c.getContext('2d');
        // initial blue
        ctx.fillStyle = "#0000FF";
        ctx.beginPath();
        ctx.moveTo(25, 0);
        ctx.lineTo(50, 50);
        ctx.lineTo(0, 50);
        ctx.lineTo(25, 0);
        ctx.fill();
        
        setTimeout(function drawRed() {
          ctx.fillStyle = "#FF0000";
          ctx.beginPath();
          ctx.moveTo(50, 0);
          ctx.lineTo(75, 50);
          ctx.lineTo(25, 50);
          ctx.lineTo(50, 0);
          ctx.fill();
        }, 500);
        
        btn.onclick = function makeItTransparent() {
          // if we weere to make this into an animation, we would set it only once
          ctx.globalCompositeOperation = 'copy';
          ctx.globalAlpha = .8;
          // draw the canvas on itself
          ctx.drawImage(c, 0, 0);
          // once again, in an animation we won't reset this to default
          ctx.globalAlpha = 1;
          ctx.globalCompositeOperation = 'source-over';
        };
        /* checkboard background */
        canvas {
          background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWNgYGBowIKxgqGgcJA5h3yFAOI3GQFqqi5ZAAAAAElFTkSuQmCC");
          }
        <canvas id="c"></canvas>
        <button id="btn">make it transparent</button>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-22
          • 1970-01-01
          • 2016-06-10
          • 1970-01-01
          • 1970-01-01
          • 2014-04-13
          • 1970-01-01
          相关资源
          最近更新 更多