【问题标题】:Display bottom canvas with a shadow effect - shadow within clipped area显示具有阴影效果的底部画布 - 剪切区域内的阴影
【发布时间】:2015-08-04 05:07:32
【问题描述】:

我是画布新手,感谢您的耐心等待。

我编写了一个引擎,它在 2 个相互重叠的画布元素中创建 2 个不同的图层。它们包含一些生成的图片,这里不重要。

我正在尝试创建一种效果,当我将鼠标移到顶层并单击时,该效果将显示底层。

类似这样的:

这是我迄今为止尝试过的:

  1. 在画布元素上使用透明度并显示底部画布(快速但不可用)
  2. 重新创建剪辑区域。
    每当我按下鼠标时,我都会存储当前坐标并使用更新的剪辑区域重新渲染画布


如果我使用描边来创建阴影,更新剪辑区域会很慢 + 我不确定如何从中删除线条(见图)。

如果我去掉阴影效果,效果会非常快,但我需要拥有它。

我想到的唯一一件事是如何加快速度,就是保存每次点击的坐标,然后将其重新计算为 1 个形状并在其上投下阴影 - 我仍然会有线条,但它会更快,因为不会有数千个圆圈要绘制...

任何帮助将不胜感激!

【问题讨论】:

    标签: javascript canvas html5-canvas


    【解决方案1】:

    您可以利用浏览器的内置插值,将其用作伪低通滤波器,但首先将其涂成黑色:

    • 将顶层复制到底层
    • 设置源输入补偿。模式
    • 全黑
    • 设置源输入补偿。模式
    • 将图片缩小到 25%
    • 将 25% 的区域缩放回原来的 50%(或当前的两倍)
    • 将现在 50% 的区域缩放回原来的 100%。它会变得模糊。

    根据您想要的模糊程度,您可以添加额外的步骤。话虽这么说:虚影无论怎么扭扭捏捏都是一种密集的操作。例如,可以妥协只在鼠标上渲染阴影(如下面的演示)。

    示例

    使用两层的示例。顶层让您可以绘制任何东西,底部将在稍后绘制时在底部显示阴影版本。

    var ctx = document.getElementById("top").getContext("2d"),
        bctx = document.getElementById("bottom").getContext("2d"),
        bg = new Image(),
        isDown = false;
    
    bg.src = "http://i.imgur.com/R2naCpK.png";
    
    ctx.fillStyle = "#27f";
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.globalCompositeOperation = "destination-out";  // "eraser"
    
    ctx.canvas.onmousedown = function(e) {isDown = true};
    
    window.onmousemove = function(e) {
      if (!isDown) return;
      var pos = getPos(ctx.canvas, e);
      ctx.beginPath();
      ctx.moveTo(pos.x + 10, pos.y);
      ctx.arc(pos.x, pos.y, 10, 0, 2*Math.PI);         // erase while drawing
      ctx.fill();  
    };
    
    window.onmouseup = function(e) {
      if (isDown) {
        isDown = false;
        makeShadow();
      }
    };
    
    function makeShadow(){
      var w = bctx.canvas.width,
          h = bctx.canvas.height,
          offset = 7, 
          alpha = 0.75;
      
      // reset alpha
      bctx.globalAlpha = 1;
    
      // normal comp mode to clear as it is faster than using "copy"
      bctx.globalCompositeOperation = "source-over";
      bctx.clearRect(0, 0, w, h);
    
      // copy top-layer to bottom-layer
      bctx.drawImage(ctx.canvas, 0, 0);
      
      // comp. mode will only draw in to non-alpha pixels next
      bctx.globalCompositeOperation = "source-in";
      
      // black overlay
      bctx.fillRect(0, 0, w, h);
      
      // copy mode so we don't need an extra canvas
      bctx.globalCompositeOperation = "copy";
      
      // step 1: reduce to 50% (quality related - create more steps to increase blur/quality)
      bctx.drawImage(bctx.canvas, 0, 0, w, h, 0, 0, w * 0.5, h * 0.5);
      
      bctx.drawImage(bctx.canvas, 0, 0, w * 0.5, h * 0.5, 0, 0, w * 0.25, h * 0.25);
      bctx.drawImage(bctx.canvas, 0, 0, w * 0.25, h * 0.25, 0, 0, w * 0.5, h * 0.5);
      
      // shadow transparency
      bctx.globalAlpha = alpha;
      
      // step 2: draw back up to 100%, draw offset
      bctx.drawImage(bctx.canvas, 0, 0, w * 0.5, h * 0.5, offset, offset, w, h);
    
      // comp in background image
      bctx.globalCompositeOperation = "destination-over";
      bctx.drawImage(bg, 0, 0, w, h);
    }
    
    function getPos(canvas, e) {
      var r = canvas.getBoundingClientRect();
      return {x: e.clientX - r.left, y: e.clientY - r.top};
    }
    div {position:relative;border:1px solid #000;width:500px;height:500px}
    canvas {position:absolute;left:0;top:0}
    #bottom {background:#eee}
    <div>
      <canvas id="bottom" width=500 height=500></canvas>
      <canvas id="top" width=500 height=500></canvas>
    </div>

    【讨论】:

    • 天啊,这看起来太棒了!稍后我需要检查它,但谢谢!
    • 为酷炫的效果和不错的合成应用点赞。顺便说一句,如果您执行以下操作,您的演示会更酷:#bottom {background-image:url('someCoolBackground.png');} 而不是#eee。 :-)
    • 只是想让您知道这对您有很大帮助。我在两层都有照片,所以我必须为阴影创建第三张。我将底层复制到新层,并在其上运行您的效果。你认为只用两个就可以完成吗?
    • @GoranUsljebrka 没问题!是的,照常画阴影,然后把底层的comp模式改成destination-over,再把底图画进去(记得切换回source-over)。 (更新答案)
    • 我看到你更新了一个带有背景图片的演示。在我的例子中,我有一个生成的框架(its not just a picture, but unique frame), which takes a bit of resources to be created, so I think (I didnt 测试它),its "cheaper" to copy it to another one, then to re-create it... I will really need to test this all in detail (at some point) but for now Im 很高兴它可以正常工作。 :-)
    猜你喜欢
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    • 2015-06-13
    相关资源
    最近更新 更多