Radius Line Appears on Moving Arc

Tags: , , ,



I was trying to create these shapes moving, the shape is made up of a semicircle and a symmetric upper arc & lower arc. They should be just the shape in front but now there are line like a tail dragging behind when they move. The output shape with unknown tail

It seems like these lines are from the moveTo part of upper and lower arcs, but I can’t figure out how to solve it. Where should I change to get rid of it?

function Fish(x, y, dx, dy, radius){

    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.radius = 30;
    
    this.draw = function(){

        c.beginPath();
        c.arc(this.x/0.6, this.y, this.radius, Math.PI * 1.5, Math.PI * 0.5, false)
        
        //Upper Arc
        c.moveTo(this.x, this.y);
        c.arc(this.x/0.6, this.y+(3*this.radius), this.radius*4, Math.PI * 229/180, Math.PI * 1.5, false)
        
        //Lower Arc
        c.moveTo(this.x, this.y);
        c.arc(this.x/0.6, this.y-(3*this.radius), this.radius*4, Math.PI * 131/180 , Math.PI * 0.5, true)
        c.strokeStyle = "green";
        c.stroke();

    }

Answer

This is because the arc method internally traces a lineTo from the current pointer’s position to the beginning of the arc (defined by cx, cy and startAngle).

To workaround that, you need to moveTo that position.

Here is a simpler demonstration using a demi-circle with the startAngle set at 0 rad:

const canvas = document.createElement( "canvas" );
document.body.append( canvas );
const ctx = canvas.getContext( "2d" );
ctx.lineWidth = 2;

const cx = 50;
const cy = 50;
const rad = 30;

ctx.beginPath();
ctx.moveTo( cx, cy );
ctx.arc( cx, cy, rad, 0, Math.PI );
ctx.strokeStyle = "red";
ctx.stroke();

ctx.translate( 80, 0 );
const first_point_x = cx + rad; // startAngle is 0
                                // so we just have to add 'rad'
                                // to find the x coord
ctx.beginPath();
ctx.moveTo( first_point_x, cy );
ctx.arc( cx, cy, rad, 0, Math.PI );
ctx.strokeStyle = "green";
ctx.stroke();

So you would have to calculate the coordinates of the point where your arc will start and moveTo that point.
This is doable, but I’m not the best with trigo and your values are quite complex, so instead, here is a workaround using Path2D objects.
If the arc command is the first of the sub-path, it will directly moveTo that initial point (since there isn’t a “current pointer’s position” yet).
So we can initialize all our arcs as standalone Path2D objects, made only of these arc commands. Then we just have to merge these Path2D objects into a last one and draw that:

const canvas = document.createElement("canvas");
document.body.append(canvas);
const c = canvas.getContext("2d");
c.lineWidth = 2;
const fish = new Fish(150, 50, 50, 50, 50);
fish.draw();

function Fish(x, y, dx, dy, radius) {

  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this.radius = 30;

  this.draw = function() {
    const p1 = new Path2D();
    p1.arc(this.x / 0.6, this.y, this.radius, Math.PI * 1.5, Math.PI * 0.5, false)
    //Upper Arc
    const p2 = new Path2D();
    p2.arc(this.x / 0.6, this.y + (3 * this.radius), this.radius * 4, Math.PI * 229 / 180, Math.PI * 1.5, false)
    //Lower Arc
    const p3 = new Path2D();
    p3.arc(this.x / 0.6, this.y - (3 * this.radius), this.radius * 4, Math.PI * 131 / 180, Math.PI * 0.5, true)
    // merge in a single Path2D object

    const path = new Path2D();
    path.addPath(p1);
    path.addPath(p2);
    path.addPath(p3);
    
    c.strokeStyle = "green";
    c.stroke(path);
    
  }
}

But in your case you can achieve the expected result quite easily, by changing the order in which you draw your Path and by never calling moveTo.

const canvas = document.createElement("canvas");
document.body.append(canvas);
const c = canvas.getContext("2d");
c.lineWidth = 2;
const fish = new Fish(150, 50, 50, 50, 50);
fish.draw();


function Fish(x, y, dx, dy, radius) {

  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this.radius = 30;

  this.draw = function() {

    c.beginPath();
    c.arc(this.x / 0.6, this.y, this.radius, Math.PI * 1.5, Math.PI * 0.5, false)
    // Lower Arc    
    c.arc(this.x / 0.6, this.y - (3 * this.radius), this.radius * 4, Math.PI * 0.5, Math.PI * 131 / 180, false)
    // Upper Arc
    // (inverse startAngle and endAngle + switch swipe to false)
    c.arc(this.x / 0.6, this.y + (3 * this.radius), this.radius * 4, Math.PI * 229 / 180, Math.PI * 1.5, false)

    c.strokeStyle = "green";
    c.stroke();

  }
}


Source: stackoverflow