I wanted to draw some circles on the canvas with the following layout: A circle right in the centre of the canvas and four circles at the mid points of the four edges of the canvas. I've put up my code here jsfiddle. The circles are rendered, however there are multiple filled paths between them too, resulting in an amalgamated mess.
I'm drawing all the circles by using
ctx.arc(points[i].x, points[i].y, radius, 0, 2 * Math.PI, true);
How do I remove them ?
EDIT: working jsfiddle
Put closePath after each circular-arc so that the circles are not connected:
ctx.arc(points[i].x, points[i].y, radius, 0, 2 * Math.PI, true);
ctx.closePath();
You are probably wanting to fill after each arc.
Something like this should work
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, true);
ctx.fill();
for(var i = 0; i < points.length; i++){
var _ = points[i];
ctx.beginPath();
ctx.arc(_.x, _.y, 25, 0, 2 * Math.PI, true);
ctx.fill();
}
Related
I'm trying to create the Fibonacci spiral with the canvas element in HTML5.
This is my drawing code, this function gets called for every number in the Fibonacci sequence. Side is the square's side length, or the current Fibonacci number. And start is the bottom right corner of each square.
function drawSpiral(side, start) {
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(start.x, start.y - side);
ctx.lineTo(start.x - side, start.y - side);
ctx.lineTo(start.x - side, start.y);
ctx.lineTo(start.x, start.y);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
switch (direction) {
case 0:
ctx.arc(start.x, start.y - side, side, Math.PI, 1.5 * Math.PI, true);
break;
case 1:
ctx.arc(
start.x - side,
start.y - side,
side,
1.5 * Math.PI,
2 * Math.PI,
true
);
break;
case 2:
ctx.arc(start.x - side, start.y, side, 2 * Math.PI, Math.PI / 2, true);
break;
case 3:
ctx.arc(start.x, start.y, side, Math.PI / 2, Math.PI, true);
break;
}
ctx.stroke();
ctx.closePath();
}
Direction is a number, 0 to 4, that represents the direction of the next square, counterclockwise - 0 = right, 1 = up, ...
It draws the squares and arcs just fine, but it's also drawing half circles everywhere, and I can't figure out why.
Screenshot of the issue
The last param is "counterclockwise",
ctx.arc(x, y, radius, startAngle, endAngle, true);
will do the same as
ctx.arc(x, y, rad, endAngle, startAngle);
// note how start and end have been swapped
that is to trace an arc from endAngle to startAngle, so in your case, it will draw the 3/4 of a circle:
const ctx = document.querySelector("canvas").getContext("2d");
const startAngle = Math.PI; // 9 o'clock
const endAngle = Math.PI * 1.5; // 12 o'clock
ctx.arc(50, 50, 50, startAngle, endAngle, true);
ctx.strokeStyle = "green";
ctx.stroke();
ctx.beginPath();
ctx.arc(160, 50, 50, endAngle, startAngle);
ctx.strokeStyle = "blue";
ctx.stroke();
<canvas></canvas>
If you want to trace only the 1/4 of the circle, then don't set this param.
Am I meant to draw the eyes in an alternative way for it to follow the cursor? Please help :) I am completely lost from here and have tried online solutions but they all require css in which my code doesn't. I want to run all of this purely from javascript, any tips?
function drawEyes() {
const c = document.getElementById("canvasEyes")
const ctx = c.getContext('2d');
//left eye
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, false);
ctx.stroke();
//iris
ctx.beginPath();
ctx.arc(75, 75, 30, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
//pupil
ctx.beginPath();
ctx.arc(75, 75, 15, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
//right eye
ctx.beginPath();
ctx.arc(225, 75, 50, 0, Math.PI * 2, false);
ctx.stroke();
//iris
ctx.beginPath();
ctx.arc(225, 75, 30, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
//pupil
ctx.beginPath();
ctx.arc(225, 75, 15, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
Basic 2D eyes that follow mouse
The eyes follow mouse by scaling the mouse coordinates to the range of motion that the iris & pupil have within the radius of the eye.
The lookat position is relative to the top left of the canvas and assumes that the eyes are at the center of the canvas.
The scaled lookat position is then set relative to the canvas center (center of both eyes)
To prevent the iris & pupil from being drawn outside the eye use the canvas clip function to clip the iris & pupil if outside the circles of the eye.
More details
It is possible to add more details
Consider adding shading, highlights, eyelids, blink, etc.. to give the animation more depth and life, for instance...
Spheres
Eyes are spheres, you can use ellipses to draw the iris & pupil, Flattening the ellipses of the iris & pupil as they get near the edge, also rotate the ellipse in the direction of the mouse. This will make the eyes look rounder in the 3rd dimention.
Example
Basic 2D eyes. See comments for details
const ctx = canvas.getContext("2d");
// Object to hold mouse coords
const lookat = {x: 150, y: 75};
// details need to make eye look at mouse coords
const eye = {
radius: 50,
iris: 30,
// limits of movement
limMin: -0.1,
limMax: 1.1,
};
// add mouse move listener to whole page
addEventListener("mousemove",e => {
// make mouse coords relative to the canvas ignoring scroll in this case
const bounds = canvas.getBoundingClientRect();
lookat.x = e.pageX - bounds.left;// - scrollX;
lookat.y = e.pageY - bounds.top;// - scrollY;
ctx.clearRect(0, 0, 300, 150);
drawEyes(lookat);
});
drawEyes(lookat);
function drawEyes(lookat) {
var {x,y} = lookat;
// normalise lookat range from 0 to 1 across and down canvas
x /= canvas.width;
y /= canvas.height;
// limit eye movement to -0.1 to 1.1 or what ever you prefer
x = x < eye.limMin ? eye.limMin : x > eye.limMax ? eye.limMax : x;
y = y < eye.limMin ? eye.limMin : y > eye.limMax ? eye.limMax : y;
// move lookat so that 0.5 is center
x -= 0.5;
y -= 0.5;
// get range of movement of iris
const range = (eye.radius - eye.iris) * 2;
// scale the lookats to the range of movement
x *= range;
y *= range;
// draw outer eyes left, right
ctx.beginPath();
ctx.arc(75, 75, eye.radius, 0, Math.PI * 2, false);
ctx.moveTo(225 + eye.radius, 75);
ctx.arc(225, 75, eye.radius, 0, Math.PI * 2, false);
ctx.stroke();
// use eyes to create a clip so iris does not draw outside the eye.
// first save canvas state so clip can be turned off at end
ctx.save();
// turn on clip which will use the two circles currently the active path
ctx.clip();
// draw iris & pupil are offset by x,y within the clip
//iris left, right
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(75 + x, 75 + y, eye.iris, 0, Math.PI * 2, false);
ctx.moveTo(225 + x + eye.iris, 75 + y);
ctx.arc(225 + x, 75 + y, eye.iris, 0, Math.PI * 2, false);
ctx.fill();
//pupil left, right
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(75 + x, 75 + y, 15, 0, Math.PI * 2, false);
ctx.moveTo(225 + x + 15, 75 + y);
ctx.arc(225 + x, 75 + y, 15, 0, Math.PI * 2, false);
ctx.fill();
// turn the clip off by restoring canvas state
ctx.restore();
}
<canvas id="canvas" width="300" height="150"></canvas>
This question already has an answer here:
All html canvas shapes are given the color of the last object added
(1 answer)
Closed 2 years ago.
I am drawing a filled ellipse (using arc()) on the javascript canvas:
ctx.arc(centerX, centerY, dotDiameter / 2, 0, 2 * Math.PI, false);
ctx.fillStyle = color1;
ctx.fill();
I then want to draw a line, that is clipped to this ellipse, in a different color.
ctx.clip(); // Clip to the ellipse
ctx.strokeStyle = color2;
ctx.moveTo(centerX - dotRadius, centerY);
ctx.lineTo(centerX + dotRadius, centerY);
ctx.stroke();
However, the ellipse is also stroked with color2.
How can I clip lines to my ellipse, but not have the ellipse stroked? Is there a way to remove the ellipse (aka arc) from the canvas after I call clip()?
Thanks!
(FYI, this is an oversimplification of my code. The lines that stroked are more complicated than a single horizontal line and the lines do need to be clipped.)
We can calculate a point on the perimeter of an ellipsis with some basic trigonometry...
ctx.moveTo(x, y);
x += radiusX * Math.cos(lineangle)
y += radiusY * Math.sin(lineangle)
ctx.lineTo(x, y);
See code snippet below, I'm drawing an ellipse and a line from center to edge.
ctx = document.getElementById('c').getContext('2d');
ctx.lineWidth = 2;
function draw(x, y, radiusX, radiusY, lineangle, color1, color2) {
ctx.beginPath()
ctx.ellipse(x, y, radiusX, radiusY, 0, 0, 2 * Math.PI, false);
ctx.fillStyle = color1;
ctx.fill();
ctx.beginPath()
ctx.strokeStyle = color2;
ctx.moveTo(x, y);
x += radiusX * Math.cos(lineangle)
y += radiusY * Math.sin(lineangle)
ctx.lineTo(x, y);
ctx.stroke();
}
angle = 0
function loop() {
ctx.clearRect(0,0, 500, 500);
draw(80, 80, 70, 50, angle, "red", "lime")
draw(240, 60, 80, 30, angle*0.7, "black", "cyan")
draw(360, 80, 30, 70, angle*2, "white", "black")
angle += 0.05
}
setInterval(loop, 100)
<canvas height="160" width="500" id="c">
This should be without this diagonal. I want to draw two arcs one with x2 smaller radius, join them and fill(). I know how to calculate the end and beginning of an arc, problem is arcs are always drawn clockwise, so to draw second arc within same ctx.beginPath() I have to use something like ctx.moveTo() but problem with moveTo is I get two different paths as proof closePath() cause this diagonal.
So I want to know how to draw arc in other direction than clockwise or how to use moveTo() but still beeing able to fill this shape. Filling now cause following issue:
White diagonal visible.
Arcs are not always drawn clockwise. The sixth parameter to .arc is a boolean which when true will make the arc be drawn anti-clockwise.
However you will also need to reverse the start and end angles, otherwise the arc will attempt to go the long way around:
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(20, 20, 40, 30 * Math.PI / 180, 80 * Math.PI / 180);
ctx.arc(20, 20, 150, 80 * Math.PI / 180, 30 * Math.PI / 180, true);
ctx.closePath();
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = '#e0e0ff';
ctx.fill();
<canvas id="c" width="200" height="200" />
Why not use the lineWidth property to make the arc thicker. In this case you'll only need one call to arc:
let canvas = document.getElementById("canvas"),
context = canvas.getContext("2d");
context.beginPath();
context.arc(20, 20, 40, 0, Math.PI / 4);
context.lineWidth = 15;
context.stroke();
<canvas id="canvas"></canvas>
This is my jsfiddle:
http://jsfiddle.net/c4upM/103/
I have the gray arc and the blue/green arc.
I am trying to make them more Elliptic, like:
and I didn't succeed.
I read this one: http://jsbin.com/ovuret/2/edit and tried to make it on my jsfiddle but I didn't succeed because the arc-s are painted in the function of: DrawCircle and DrawEllipseForProjection.
There are two functions: blueArc and grayarc. by the radian angle, there is a calculation in function circleInArc that places the small lightblue circle and the small gray circle (when you mouse over the canvas) in the arc-s.
I want these functions to work after the change but I didn't succeed.
these are the blue and gray arc-s:
function grayArc(strokeColor, cx, cy, radius, ctx, linewidth) {
ctx.beginPath();
ctx.arc(cx, cy, radius, Math.PI, Math.PI * 2);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokeColor;
ctx.stroke();
}
function blueArc(strokeColor, radianStart, radianEnd, cx, cy, radius, ctx, linewidth) {
ctx.beginPath();
ctx.arc(cx, cy, radius, radianStart, radianEnd);
ctx.lineWidth = linewidth;
ctx.strokeStyle = strokeColor;
ctx.stroke();
}
and this is the circleInArc function:
function circleInArc(fillColor, radianAngle, cx, cy, radius, ctx, linewidth) {
var x = cx + radius * Math.cos(radianAngle);
var y = cy + radius * Math.sin(radianAngle);
ctx.beginPath();
ctx.arc(x, y, linewidth / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = fillColor;
ctx.fill();
return ({
x: x,
y: y,
radius: linewidth / 2
});
}
Any help appreciated!
You can try draw arc with bigger radius and not from Math.PI to 2*Math.PI but this one
ctx.arc(cx, cy, radius, Math.PI*5/4, Math.PI * 7/4);
and don't forget move the arc down for radius size
grayArc("rgb(177,177,177)", cx, cy+radius, radius*2, ctx, linewidth);
hope it will help
I've fiddled around a bit, and you'll propably have to adjust some of the values, but here's an example: Fiddle (Only the gray arc was adjusted)
The basic strategy is to save the state of the context, then scale it to your liking. If you only scale one axis.. voilá, ellipsis.
Also, I translated the origin of the canvas to the center of the arc before scaling, because they get scewed with the scaling. The center of the arc is then 0, 0.
After that the context can be restored like nothing ever happened, an you can stroke your transformed arc.
ctx.save();
ctx.translate(cx,cy);
ctx.scale(1.3, 1);
ctx.arc(0, 0, radius, 1.2*Math.PI, 1.8*Math.PI);
ctx.restore();
You'll propably want to play around with the radius, the ratio of scaling and maybe the range in which the arc gets drawn to fit your needs.
Edit: I cut down on the range of the arc which gets drawn, to prevent the stubby ends of the ellipsis showing. From 1.2*PI to 1.8*PI instead of 1*PI to 2*PI seems to look alright in the current case. :)
For future questions, please try to extract the a more basic example of your problem, to make it easier for those who want to help out. :)