【问题标题】:Canvas 2DPath Clip overlap prCanvas 2DPath Clip 重叠pr
【发布时间】:2021-04-06 09:40:35
【问题描述】:

我正在尝试在画布中创建一个复合形状来掩盖它下面的所有内容 - 我将为这些路径设置动画,以便它们最终相交 - 问题是,当它们相交时,它们会在绘图重叠。我只想要一个纯色面具。

let region = new Path2D();
            
            // first line
            //ctx.transform(1,0,0,1,xShift,yShift);
            region.moveTo(wdth*(.093+p1x) + p1x, ((ht*0)));
            region.lineTo(wdth*(.096+p2x)  + p2x, ((ht*0)));
            region.lineTo(wdth*(.302+p2x)   + p1x, ((ht*1)));
            region.lineTo(wdth*(.299+p1x), ((ht*1)));
    ctx.save();
            //ctx.fill(region);

            //
            ctx.beginPath();
            region.moveTo(wdth*.326, ((ht*1)));
            region.lineTo(wdth*.329, ((ht*1)));
            region.lineTo(wdth*.537, ((ht*0)));
            region.lineTo(wdth*.534, ((ht*0)));
    ctx.save();
            //ctx.fill(region);
            //
    ctx.beginPath();
            region.moveTo(wdth*.680, ((ht*0)));
            region.lineTo(wdth*.683, ((ht*0)));
            region.lineTo(wdth*.464, ((ht*1)));
            region.lineTo(wdth*.461, ((ht*1)));
    ctx.save();
            //ctx.fill(region);
            //
    ctx.beginPath();
            region.moveTo(wdth*.926, ((ht*1)));
            region.lineTo(wdth*.929, ((ht*1)));
            region.lineTo(wdth*.702, ((ht*0)));
            region.lineTo(wdth*.699, ((ht*0)));
            ctx.fill(region);
            //ctx.beginPath();
            //
            ctx.clip(region, "nonzero");

这是一个示例(如您所见,有一条细线重叠,未正确剪裁 - 但另一条线剪裁正确):

https://codepen.io/pdub888/pen/MWjrdwe

【问题讨论】:

  • 您最好使用隐藏的画布并正常绘制所有形状,然后使用globalCompositeOperation 在最终画布上进行大蒙版。可能效率更高,您可以在调试器中直观地检查您的工作。
  • 同理/示例,使用 globalCompositeOperation:codepen.io/pdub888/pen/vYXpwmv 仍然是那个奇怪的重叠问题。
  • 不是那样,使用默认操作(源覆盖)绘制每条路径,然后在最终画布上绘制缓冲画布,并说“目的地输出”(其中之一,无法检查atm) 用缓冲区的 alpha 遮盖最终的画布。
  • 同样的问题(我想我正在做你要求的事情):codepen.io/pdub888/pen/ZEpvdGx
  • 查看我的答案,我从 codepen 修改了您的 sn-p,如果由于缠绕算法而没有将其绘制为单条路径,它确实有效(另请参见:developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule)

标签: javascript canvas html5-canvas


【解决方案1】:

你应该做的是在一个单独的 - 隐藏 - 缓冲区画布中绘制它,只需填充所有形状 - 不需要 Path2D 构造函数,除非你将重用它,但即使那样你也不应该像这样绘制整个形状(由于缠绕算法,某些位不是填充的一部分,但这与此处的屏蔽图像并不完全相关 - 更多信息和示例:https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule)

然后使用该缓冲区画布使用globalCompositeOperation 剪切遮罩另一个画布。下面我修改了你的sn-p,去掉了构造函数,直接作用在缓冲画布上。

document.addEventListener("DOMContentLoaded", function(event) {
      let canvas = document.getElementById('mainmask');
      let gradcanvas = document.getElementById('gradmask');
            let aWrapper = document.querySelector('.canvas-wrap');
            let ctx = canvas.getContext('2d');
      let ctxGrad = gradcanvas.getContext('2d');
            let win = {};
            let markerShow = false;

            // for reszing canvas 
            function setCanvasScalingFactor() {
               return window.devicePixelRatio || 1;
            }
            
            function resizeViaCanvas() {
                //Gets the devicePixelRatio
                var pixelRatio = setCanvasScalingFactor();
                //The viewport is in portrait mode, so var width should be based off viewport WIDTH
                if (window.innerHeight < window.innerWidth) {
                    //Makes the canvas 100% of the viewport width
                    var width = Math.round(1.0 * window.innerWidth);
                }
                //The viewport is in landscape mode, so var width should be based off viewport HEIGHT
                else {
                    //Makes the canvas 100% of the viewport height
                    var width = Math.round(1.0 * window.innerHeight);
                }
                //This is done in order to maintain the 1:1 aspect ratio, adjust as needed
                //var height = width*.5625;
                var height = Math.round(1.0 * window.innerHeight);
                //This will be used to downscale the canvas element when devicePixelRatio > 1
                aWrapper.style.width = width + "px";
                aWrapper.style.height = height + "px";
                canvas.width = width * pixelRatio;
                canvas.height = height * pixelRatio;
        gradcanvas.width = width * pixelRatio;
                gradcanvas.height = height * pixelRatio;
            }
            // two colors for mask gradient
            //let color1 = [201, 237, 223]
            let color1 = [5, 5, 5]
            let color2 = [226, 124, 99]
            
            // various rgb colors
            let mint = [201, 237, 223]
            let red = [239, 65, 35]
            let fadedred = [226, 124, 99]
            let purple = [28, 16, 42]
            let beige = [255, 236, 173]
            let peach = [251, 199, 143]

            
            let scaleAmt = 1;
            // where the mask is drawn based on tweening scaleAmt
            function drawMask() {
                //the length of each square
                var w = Math.round(canvas.width);
                var h = Math.round(canvas.height);
        console.log( w + "WIDTH" + h + "HEIGHT")
                ctxGrad.clearRect(0, 0, canvas.width, canvas.height);
                ctxGrad.save();
                // ctx.fillStyle = `rgba(239, 65, 35, ${viaAlpha.alpha})`;
                ctxGrad.fillStyle = `"rgb(" + color1[0] + "," + color1[1] + "," + color1[2] + ")"`
                //

                
                let wdth = w;
                let ht = h;
                let xShift = 0;
                let yShift = 0;
                
                let p1x = -.1;
                let p2x = .1;
        
        var gradient = ctxGrad.createLinearGradient(0, 0, w, 0);
                gradient.addColorStop(.4, "rgb(" + color1[0] + "," + color1[1] + "," + color1[2] + ")");
                gradient.addColorStop(1, "rgb(" + color2[0] + "," + color2[1] + "," + color2[2] + ")");
                ctxGrad.fillStyle = gradient;
                ctxGrad.fillRect(0, 0, w, h);
        ctxGrad.save();
        
        
        ctx.beginPath();
                ctx.lineWidth = 0;
                ctx.fillStyle = 'blue';
                // first line
                //ctx.transform(1,0,0,1,xShift,yShift);
                ctx.moveTo(wdth*(.093), ((ht*0)));
                ctx.lineTo(wdth*(.096), ((ht*0)));
                ctx.lineTo(wdth*(.302), ((ht*1)));
                ctx.lineTo(wdth*(.299), ((ht*1)));
        ctx.fill();
        //ctx.save();


                //
                ctx.beginPath();
                ctx.moveTo(wdth*(.326+p1x), ((ht*1)));
                ctx.lineTo(wdth*(.329+p2x), ((ht*1)));
                ctx.lineTo(wdth*(.537+p2x), ((ht*0)));
                ctx.lineTo(wdth*(.534+p1x), ((ht*0)));
        ctx.fill();

                //
        ctx.beginPath();
        
                ctx.moveTo(wdth*.680, ((ht*0)));
                ctx.lineTo(wdth*.683, ((ht*0)));
                ctx.lineTo(wdth*.464, ((ht*1)));
                ctx.lineTo(wdth*.461, ((ht*1)));
        ctx.fill();


                //
        ctx.beginPath();
                ctx.moveTo(wdth*.926, ((ht*1)));
                ctx.lineTo(wdth*.929, ((ht*1)));
                ctx.lineTo(wdth*.702, ((ht*0)));
                ctx.lineTo(wdth*.699, ((ht*0)));
                ctx.fill();

                //ctx.beginPath();
                //
                //ctx.clip(region, "nonzero");
                //ctx.clip();
        
        ctxGrad.globalCompositeOperation='destination-in';
        ctxGrad.drawImage(canvas, 0, 0);
                
                ctx.setTransform(1,0,0,1,0,0);
                

                //ctx.restore();
            }

//window.addEventListener("resize", resizeViaCanvas, false);
resizeViaCanvas();
drawMask();
});
      
.canvas-wrap{
        position:fixed;
        width:100vw;
        height:100vh;
        top:0;
        margin: 0 auto;
        left: 50%;
        transform: translate(-50%, -50%);
        top: 50%;
        pointer-events: none;
        opacity:1;
    border:1px solid blue;
    background:transparent;
}

#mainmask{
  display: block;
  width: 100%;
  height: 100%;
  opacity:.1;
  position:absolute;
  display:none;
}

#gradmask{
  display: block;
  width: 100%;
  height: 100%;
  background:green;
  position:absolute;
}
<div class="wrapper">

        <div class="canvas-wrap">
       <canvas id="gradmask">
      </canvas>
            <canvas id="mainmask">
            </canvas>
        </div>

    </div>

【讨论】:

  • 有趣。我正在查看我最新的 codepen 中的代码(具有两个画布元素的那个),看起来您大多只是从 2DPath 元素中删除了它,对吧?好吧,这看起来确实有效....现在我可以自定义动画所有这些点。乐趣。感谢您今天的帮助。
  • 是和否 - 复合操作也发生了变化(玩这个,mdn 在这里有一个 grrat 备忘单:developer.mozilla.org/en-US/docs/Web/API/…)另外,不要忘记您可以使用转换来完成大部分操作动画的重任!滑动、挤压、旋转。请记住,您始终可以使用缓冲区来绘制下一个。这可能是最快的使用画布的方法了。
  • 好吧,我认为这很好 - 但是使用请求动画帧调用 drawMask(绘制两个画布,然后在主画布上使用复合材料绘制/遮罩)是否存在问题? (即我需要保存一些东西还是清除一些东西?)。它在第一次调用后消失(注释掉复合并且它“工作”或更确切地说是绘制):codepen.io/pdub888/pen/abmEevO
  • *没关系:对于将来使用此代码的任何人,请务必将主画布上的 globalComposite 重置为操作顶部的“source-over” - 否则它将继续写入和之前的合成一样。
猜你喜欢
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多