HTML5 Canvas ctx.clip() method used on a circle leaves a line underneath the circle

前端 未结 1 382
[愿得一人]
[愿得一人] 2020-12-07 00:21

I\'ve created a program to generate planet sprites. I\'m doing that by creating a circular path, running ctx.clip() to keep all the following layers inside of t

相关标签:
1条回答
  • 2020-12-07 00:34

    I am not sure I do understand your problem, but I will assume that you are talking about the anti-aliasing problem.

    Currently, you are drawing a lot over your clipped area.
    At each draw, new anti-aliasing artifacts will come to smooth the latest drawing. At the end, what should have been semi-transparent pixels are now fully opaque ones.
    In the other hand, with globalCompositeOperation like 'destination-in', you need only one drawing to make the compositing (~clipping). So you don't accumulate artifacts. But even if you did, gCO is global and since it takes transparency into account, the accumulation would be less important.

    var ctx1 = clip.getContext('2d');
    var ctx2 = gCO.getContext('2d');
    var ctx3 = gCO2.getContext('2d');
    
    ctx1.beginPath();
    ctx1.arc(150, 150, 150, 0, Math.PI*2)
    ctx1.clip();
    // drawing multiple times on this clipped area will increase artifacts
    ctx1.fillRect(0,0,300, 150);
    ctx1.fillRect(0,0,300, 150);
    ctx1.fillRect(0,0,300, 150);
    ctx1.fillRect(0,0,300, 150);
    
    ctx2.beginPath();
    ctx2.arc(150, 150, 150, 0, Math.PI*2)
    ctx2.fillRect(0,0,300, 150);
    ctx2.globalCompositeOperation = 'destination-in';
    //With gCO you only draw once, but even if you did draw multiple times, there would still be less artifacts
    ctx2.fill();
    ctx2.fill();
    ctx2.fill();
    ctx2.fill();
    ctx2.globalCompositeOperation = 'source-over';
    
    ctx3.beginPath();
    ctx3.arc(150, 150, 150, 0, Math.PI*2)
    ctx3.fillRect(0,0,300, 150);
    ctx3.globalCompositeOperation = 'destination-in';
    // only one drawing needed:
    ctx3.fill();
    ctx3.globalCompositeOperation = 'source-over';
    
    ctx1.fillStyle = ctx2.fillStyle = ctx3.fillStyle = "white";
    ctx1.fillText('clipping', 120, 100);
    ctx2.fillText('compositing', 120, 100);
    ctx3.fillText('single compositing', 120, 100);
    canvas{
      border: 1px solid;
      }
    <canvas id="clip"></canvas><canvas id="gCO"></canvas><canvas id="gCO2"></canvas>

    A few unrelated notes about your code :

    closePath does not mark the end of your path declaration, only a new beginPath() call does. ctx.fillStyle = 'transparent'; ctx.fill() won't do anything. Only putImageData, clearRect methods and globalCompositeOperation + drawing method can produce transparent pixels.

    So here is all the above in one snippet :

    /* Load images */
    var texture = new Image();
    texture.src = "http://i.imgur.com/0qMwa8p.png";
    var shadow = new Image();
    shadow.src = "http://i.imgur.com/pX3HVFY.png";
    
    /* Create the canvas and context references */
    var canvas = document.getElementById("game");
    canvas.style.width = (canvas.width = 512) + "px";
    canvas.style.height = (canvas.height = 512) + "px";
    var ctx = canvas.getContext("2d");
    
    /* render */
    function render() {
      /* Size of planets */
      var scale = Math.random() + 1
    
      
      // We don't need to save/restore the canvas state now,
      // simply remember to set the gCO back to 'source-over'
      // here it done at the end of the function
      
      /* Clear canvas for redraw */
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      /* Place texture onto planet */
      ctx.globalAlpha = Math.random() * .5 + .5;
      ctx.drawImage(texture, (Math.round(Math.random() * 256) - 128 * scale), (Math.round(Math.random() * 256) - 128 * scale), texture.naturalWidth * scale, texture.naturalHeight * scale)
    
      /* Color Planet */
      ctx.globalAlpha = 1;
      ctx.globalCompositeOperation = "multiply";
      var color = "hsl(" + Math.random() * 256 + ", 100%, 50%)"
      ctx.fillStyle = color;
      ctx.fillRect(0, 0, canvas.width, canvas.height)
    
      /* Give planet its shine and shadow */
      ctx.globalCompositeOperation = "source-over";
      ctx.drawImage(shadow, Math.round(Math.random() * 200 - 128 * scale), Math.round(Math.random() * 200 - 128 * scale), shadow.naturalWidth * scale, shadow.naturalHeight * scale)
    
      // instead of clipping, use gCO
      ctx.globalCompositeOperation = 'destination-in';
      ctx.beginPath();
      ctx.arc(256, 256, 128 * scale, 0, 2 * Math.PI);
      ctx.fill();
      // reset gCO
      ctx.globalCompositeOperation = 'source-over';
    }
    render()
    window.interval = setInterval(render, 500)
    #game {
      border: 1px solid black;
      background-color: black;
    }
    <canvas id="game"></canvas>

    0 讨论(0)
提交回复
热议问题