Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I perform clearRect() in canvas after clip()?

I need to perform clearRect() after performing clip() in canvas. Unfortunately, It does not working for me.

If I exclude clip() means, clearRect() is working fine me.

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 300, 150);
ctx.clip();  //after removing clip() clearRect() working
ctx.clearRect(20, 20, 100, 50);
</script>

I need to perform clearRect() after performing clip(). Is this possible?

Please find the fiddle

public canvasClip(options: BaseAttibutes): void {
        this.ctx.save();
        this.ctx.rect(options.x, options.y, options.width, options.height);
        this.ctx.clip();
    }


  public clearRect(rect: Rect): void {
        this.ctx.restore();
        this.ctx.clearRect(rect.x, rect.y, rect.width, rect.height);
    };

If I called clearRect() after calling canvasClip(), it did not worked for me.

thanks in advance, Kesavan

like image 701
Kesavan Subramaniam Che Avatar asked Oct 16 '22 15:10

Kesavan Subramaniam Che


1 Answers

The way clip() works is that it takes the current sub-path declaration and everything that should not be in its filled area will be discarded from the drawing zone. That is, if you draw again something outside of this area, then it won't appear on the context.

I guess your confusion comes from fillRect() though. This method is a bit special in that it creates a temporary rectangular sub-path that is only used for this method (strokeRect does the same).

Here is a visual example of what this means:

const ctx = canvas.getContext('2d');

// declare a sub-path 
ctx.beginPath();
ctx.arc(50,50,30,0,Math.PI*2); // circle

// this will discard our circle
ctx.fillStyle = 'red';
ctx.fillRect(120, 50, 30, 30);

// wait a bit to show it has been discarded
setTimeout(() => {
  ctx.fillStyle = 'blue';
  ctx.fill(); // this draws only the circle,
  // the internal rect() in fillRect() has disappeared,
  // otherwise it would be blue now
}, 1000);
canvas {border: 1px solid}
<canvas id="canvas" width="500"></canvas>

So you actually didn't declare any Sub-Path on your context before calling clip(), and the new drawing area (unclipped) is a 0x0px path (i.e it doesn't exists). Every drawing operation (and clearRect is one) will then have no effect.

const ctx = canvas.getContext('2d');

// what OP did is equivalent to
ctx.beginPath();
ctx.clip(); // clip a non-existent sub-path

// you can try to draw, it won't work
ctx.arc(50,50,30,0,Math.PI*2);
ctx.fill();
ctx.fillRect(120, 50, 30, 30);
canvas {border: 1px solid}
<canvas id="canvas" width="500"></canvas>

The only method that would still have an effect would be putImageData.

And since you can only remove drawing areas, by clipping this non-area, you actually locked your context in this state.*
So before using clip(), always call ctx.save() in order to be able to reset the clipping area afterward.
*Well it could still be unlocked by setting the canvas width or height, but that's bad m'kayy?

const ctx = canvas.getContext('2d');

// so we can reset our clipping area
ctx.save();

ctx.fillStyle = "red";
// use `rect()` which does add to the current sub-path
ctx.rect(0, 0, 300, 150);
ctx.fill();
ctx.clip();

ctx.clearRect(20, 20, 100, 50); // now we can clear

ctx.restore(); // remove the clipping area
ctx.fillRect(320, 0, 20, 20); // now we can draw anywhere
canvas {border: 1px solid}
<canvas id="canvas" width="500"></canvas>

But note that instead of clipping, most of the time, you're better using compositing which results in cleaner antialising, and often in better performances. But since I don't know why you wanted to use clip, I can't really show you the equivalent.

like image 51
Kaiido Avatar answered Oct 20 '22 15:10

Kaiido