【问题标题】:Canvas shape inner shadow + blending mode画布形状内阴影+混合模式
【发布时间】:2018-05-13 16:29:44
【问题描述】:

所以这几天让我发疯了。

我正在尝试使用不同的混合模式(如overlay)在 JS 中复制 Photoshop 的内部阴影。

有一些简单的方法可以为ctx.shadowBlurctx.filter = 'drop-shadow(...)' 等形状添加阴影,但这些方法只会生成外部阴影。您可以使用xor 使用一些合成魔法创建内部阴影,但这会使图像边缘不平滑(我猜xor 并不能很好地处理抗锯齿),就像在这个例子中一样:

https://jsfiddle.net/89pes8ap/1/

所以,我有了另一个可行的想法,因为它只使用了一次 xor

https://jsfiddle.net/3cnwtvyj/

但正如您所见,overlay-ed 版本仍然没有平滑的边缘。

那么,我的问题是:如何使用可以处理各种形状的不同混合模式添加平滑的内阴影?

【问题讨论】:

    标签: javascript canvas blending


    【解决方案1】:

    复合操作确实适用于 Alpha 通道,因此不适用于阴影。

    不幸的是,我认为对于透明背景上的阴影(尤其是圆形),您无能为力。

    我可以看到的一件小事是,您实际上不需要 xor 部分,并且可以将其替换为由矩形和弧组成的路径,您将填充为“偶数”。这将直接创建孔,减少一点抗锯齿伪影。

    drawOP();
    drawEvenOdd();
    
    
    
    function drawEvenOdd() {
      let canvas = document.getElementById('evenodd');
      canvas.width = 150;
      canvas.height = 200;
      let ctx = canvas.getContext('2d')
      ctx.textAlign = 'center';
    
      ctx.shadowColor = 'black';
      ctx.shadowBlur = 10 * 2;
      ctx.shadowOffsetY = 5;
    
      ctx.beginPath();
      ctx.rect(0, 0, canvas.width, canvas.height);
      ctx.arc(100, 100, 50, 0, 2 * Math.PI);
      // will draw an hole
      ctx.fill('evenodd');
    
      // remove shadow
      ctx.shadowColor = 'transparent';
      ctx.shadowBlur = ctx.shadowOffsetY = 0;
    
      ctx.globalCompositeOperation = 'destination-out';
      ctx.fill('evenodd');
      ctx.globalCompositeOperation = 'source-over';
      ctx.fillText('evenodd', 100, 180)
    }
    
    function drawOP() {
      let canvas = document.getElementById('OP')
      canvas.width = 150;
      canvas.height = 200;
      let ctx = canvas.getContext('2d')
      ctx.textAlign = 'center';
    
      ctx.fillStyle = 'black'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      ctx.globalCompositeOperation = 'xor'
    
      ctx.arc(100, 100, 50, 0, 2 * Math.PI)
      ctx.fill()
    
      ctx.filter = 'drop-shadow(0 5px 10px black)'
      ctx.drawImage(canvas, 0, 0)
      ctx.filter = 'none';
      ctx.fillText('OP', 100, 180)
    }
    <canvas id="OP"></canvas>
    <canvas id="evenodd"></canvas>

    【讨论】: