admin管理员组

文章数量:1425708

PEN:

    var canvas = document.getElementById('c');
    var ctx = canvas.getContext("2d");
    var centerw = canvas.width/2;
    var centerh = canvas.height/2;
    var sq_w = 80;
    //
    ctx.beginPath();
    //draw rectangle
    ctx.rect(this.centerw-(sq_w/2), 0,sq_w, canvas.height);
    //draw circle
    ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
    //fill
    ctx.fill();

The shapes both draw but the intersection of the shapes is blank.

Looking to have one single, filled shape, but get the following result:[

REQUIREMENTS: Cannot use CanvasRenderingContext2D.globalCompositeOperation as I'm using that for something else; this needs to be used as a single shape so i can use the shape to ...clip().

Note: when using two rect() calls it works, and when using two arc() calls it works, but mixing them seems to cause an issue.

Seems like it should be easy but I'm stumped, missing something basic I think. Thanks!

PEN: https://codepen.io/jaredstanley/pen/gvmNye

    var canvas = document.getElementById('c');
    var ctx = canvas.getContext("2d");
    var centerw = canvas.width/2;
    var centerh = canvas.height/2;
    var sq_w = 80;
    //
    ctx.beginPath();
    //draw rectangle
    ctx.rect(this.centerw-(sq_w/2), 0,sq_w, canvas.height);
    //draw circle
    ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
    //fill
    ctx.fill();

The shapes both draw but the intersection of the shapes is blank.

Looking to have one single, filled shape, but get the following result:[

REQUIREMENTS: Cannot use CanvasRenderingContext2D.globalCompositeOperation as I'm using that for something else; this needs to be used as a single shape so i can use the shape to ...clip().

Note: when using two rect() calls it works, and when using two arc() calls it works, but mixing them seems to cause an issue.

Seems like it should be easy but I'm stumped, missing something basic I think. Thanks!

Share Improve this question edited Feb 10, 2018 at 20:24 jared asked Feb 10, 2018 at 19:50 jaredjared 75210 silver badges19 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

Path-direction matters

Simply remove (or set to false) the counter-clock wise flag on the arc() method as this will otherwise define the path the "opposite" direction affecting the default non-zero winding algorithm used for filling:

//ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true); ->
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2);

A More Close Look at "Non-Zero Winding"

According to the non-zero winding rule we would add up winding counted from a point from where a line is "sent out". For each line intersection of the point's line we check the crossing line's direction and give it +1 for one direction, -1 if the opposite direction, and add those together.

To illustrate:

For the illustration on the left we can see that the sum of the directions of the two first line intersections (if point is placed left and center on y) will be 0 ("zero") so no fill for the center section. This would also happen if a point sent a line from center top and down through the shape.

However, in the illustration on the right the sum is non-zero when we e to the inner section so it too bees filled.

Example: arc() uses clockwise direction instead

var canvas = document.getElementById('c');
var ctx = canvas.getContext("2d");
var centerw = canvas.width/2;
var centerh = canvas.height/2;
var sq_w = 120;
//
ctx.beginPath();
//draw rectangle
ctx.rect(centerw-(sq_w/2), 0,sq_w, canvas.height);
//draw circle
ctx.moveTo(centerw + 185, centerh); // create new sub-path (is unrelated, see below)
ctx.arc(centerw, centerh, 185, 0, Math.PI * 2); // <- don't use the CCW flag
//fill
ctx.fill();
<canvas id="c" width="500" height="500"></canvas>

Unrelated but something to have in mind: you would also want to create a new sub-path for the arc to avoid risking a line from a corner of the rect going to the start-angle point on the arc. Simply add this line before adding the arc:

ctx.moveTo(centerw + 185, centerh);
ctx.arc(centerw, centerh, 185, 0, Math.PI * 2);
ctx.beginPath();
//draw rectangle
ctx.rect(this.centerw - (sq_w / 2), 0, sq_w, canvas.height);
ctx.fill();
//draw circle
ctx.beginPath();
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);
//fill
ctx.fill();

The result you see happens because the standard operation on a surface contained by crossed paths, is to ignore.

var canvas = document.getElementById('c');
var ctx = canvas.getContext("2d");
var centerw = canvas.width/2;
var centerh = canvas.height/2;
var sq_w = 80;

//draw rectangle
ctx.fillRect(this.centerw-(sq_w/2), 0,sq_w, canvas.height);

//draw circle
ctx.arc(this.centerw, this.centerh, 185, 0, Math.PI * 2, true);

//fill
ctx.fill();
<canvas id='c' height=500 width=500/>

The shapes need to be filled between the rounds. Or, in the code snippet, I changed ctx.rect to ctx.fillRect.

Another approach would be to begin a new path before the arc.

本文标签: javascriptOverlapping shapes in canvasStack Overflow