Clipping lines within ellipse on javascript canvas [duplicate] - javascript

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">

Related

How do you make the eyes move and follow the cursor? I honestly dont know what to do from here

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>

Rotate an ellipse using arc method Javascript

Using context arc method i need to rotate an arc on the canvas.
ctx.save();
ctx.beginPath();
ctx.translate(cx-rx, cy-ry);
ctx.scale(rx, ry);
ctx.rotate(angle * Math.PI / 180); // angle = 45 will move away from the cordinates
ctx.arc(1, 1, 1, 0, 2 * Math.PI, false);
ctx.restore();
ctx.stroke();
ctx.restore();

HTML5 canvas : counterclockwise arc with angle > 2 PI

I try to figure why angle > 2 PI does not give the same result when drawing an arc clockwise and counterclockwise.
Look at this code snippet, on the first line I draw "clockwise" 3 red arcs with a start angle of 0 and an end angle of PI, 2*PI and 3*PI.
Then I draw "counterclockwise" 3 blue arcs with same parameters.
The 3rd result bewilders me... Can anyone explain this to me ?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// CLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 50, 40, 0, Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 50, 40, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 50, 40, 0, 3 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// COUNTERCLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 150, 40, 0, Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 150, 40, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 0, 3 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>
According to specs:
If anticlockwise is false and endAngle-startAngle is equal to or greater than 2π, or, if anticlockwise is true and startAngle-endAngle is equal to or greater than 2π, then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
Otherwise, the points at startAngle and endAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, are the start and end points respectively, and the arc is the path along the circumference of this ellipse from the start point to the end point, going anti-clockwise if anticlockwise is true, and clockwise otherwise. Since the points are on the ellipse, as opposed to being simply angles from zero, the arc can never cover an angle greater than 2π radians.
Put in maybe clearer pseudo-code:
if(
(anticlockwise === false && (endAngle - startAngle) >= 2π) ||
(anticlockwise === true && (startAngle - endAngle) >= 2π)
) {
arc_circumference = 2π;
}
else {
startAngle = startAngle % 2π;
endAngle = endAngle % 2π;
}
In your case, startAngle = 0, endAngle = 3π, anticlowkwise = true, if we run the above algorithm, we end up in the else case (0 - 3π < 2π) and endAngle is now (3π % 2π = 1π).
We could achieve the same output without the anticlockwise flag by swapping startAngle and endAngle:
var ctx = canvas.getContext("2d");
// COUNTERCLOCKWISE, angle = -3 PI (from OP)
ctx.beginPath();
ctx.arc(50, 150, 40, 0, 3 * Math.PI, true);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// CLOCKWISE, angle = -3 PI
ctx.beginPath();
ctx.arc(50, 50, 40, 3 * Math.PI, 0);
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
<canvas id="canvas"></canvas>
When you go COUNTERCLOCKWISE, after 0 comes 2PI. You should try this instead:
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 2 * Math.PI, 0, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
UPDATE:
After OP's comment I've added an animated demo:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
let delta = 0;
function Draw(){
requestAnimationFrame(Draw)
delta+= .01;
ctx.clearRect(0,0,c.width,c.height)
// CLOCKWISE: animating the end point
ctx.beginPath();
ctx.arc(50, 50, 40, 0, delta, false);
ctx.closePath();
ctx.stroke();
// CONTERCLOCKWISE, animating the start point
ctx.beginPath();
ctx.arc(150, 50, 40, 0,-delta, true);
ctx.closePath();
ctx.stroke();
}
Draw()
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>

How can I move just one of many shapes inside canvas by 50 pixels;

I have a project to do and I cannot do it until I understand how moving object inside canvas work.
I need to move one of the objects below by 50pixels to right.
Anyone willing to help me is greatly appreciated.
Meanwhile thank you very much in advance for your help or suggestions.
function canvasOneShape() {
//refers to the html canvasone id
var canvas = document.getElementById("canvasOne");
this.canvasOne.width = 945;
this.canvasOne.height = 650;
// draws the canvas in 2d
var ctx = canvas.getContext("2d");
// Set the fill colour to blue.
ctx.fillStyle = "blue"; //used like this instead of rgb due personal preference:)
// Create a filled rectangle at co-ordinates (10,10)
// with height and width set to 100.
ctx.fillRect(10, 10, 250, 330); //
// Here I draw the square
// Set the canvas up for drawing in 2D.
// Set the fill colour to blue.
ctx.fillStyle = "rgba(244, 244, 189,.5)";
ctx.fillRect(10, 50, 330, 250);
//draw my first circle
var midXone = canvas.width / 2; //x location
var midXtwo = canvas.height / 2; //y location
var radius = 60; //circle radius
ctx.beginPath();
ctx.arc(midXone, midXtwo, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(89, 192, 227,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = '#003300';
ctx.stroke();
//draw the second circle
var midX = canvas.width / 2.35; //x location
var midY = canvas.height / 2.35; //y location
var radius = 50; //circle radius
ctx.beginPath();
ctx.arc(midX, midY, radius, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle ="rgba(255, 244, 9,.4)";
ctx.stroke();
//draw Square with circle inside
//square
ctx.fillStyle = "rgb(222, 33, 51)";
ctx.fillRect(550, 20, 300, 300);
//circle
ctx.beginPath();
ctx.arc(700, 170, 150, 0, 2 * Math.PI, false);
ctx.fillStyle ="rgba(66, 244, 89,.4)";
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#f44242';
ctx.stroke();
//The Pacman object
var radius = 100; //circle radius
var x = 100;
var y = 500;
ctx.beginPath();
ctx.arc(120, 500, radius, 1.85 * Math.PI, .15 * Math.PI, true);
//Draw mouth
ctx.lineTo(120, 500);
ctx.closePath();
ctx.fillStyle = "rgb(255, 255, 0)";
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgb(0,0,0)';
ctx.stroke();
//draw eye
ctx.beginPath();
ctx.arc(x + 40, y - 40, 10, 0 * Math.PI, 2 * Math.PI, true);
ctx.fillStyle = "rgb(0,0,0)";
ctx.fill();
}
there are no layers or objects on a canvas(it's just a single layer of pixels), so, if you draw something, you overwrite what's underneath it. to move something to the right, you will need to have a function to draw everything behind it, and then variables to store the location (x and y) of the objects you want to move.
then, to move it, you clear the canvas with
ctx.clearRect(0, 0,width of canvas, height height of canvas);,
call the background function to draw everything behind it again, and the redraw your object in a different location.

How to draw stroke inside a circle?

Is there an easy way to draw stroke inside a circle (without drawing 2 circles and similar workarounds)? If I do it this way:
context.beginPath();
context.arc(200, 200, 93, Math.PI / 2, Math.PI, true);
context.fillStyle = '#FF6A6A';
context.fill();
context.lineWidth = 20;
context.strokeStyle = '#FF0000';
context.stroke();
I get this:
The stroke is partially drawn outside the figure (marked by green circles) while I need it inside.
You should change the radius to compensate for the line width:
context.beginPath();
context.arc(200, 200, 93, Math.PI / 2, Math.PI, true);
context.fillStyle = '#FF6A6A';
context.fill();
context.lineWidth = 20;
context.strokeStyle = '#FF0000';
context.beginPath();
// the radius of 93 - half the line width
context.arc(200, 200, 93-10, Math.PI / 2, Math.PI, true);
context.stroke();
JS Bin: http://jsbin.com/qamuwumiri/edit?html,js,output

Categories

Resources