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(); }
Advertisement
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(); } }