Why properties previous circle in canvas is getting replaced with newer properties? - javascript

I have canvas, where I wanted to draw one inner and just above it one outer circle. First of some weired line is shown between in circle. Secondly, I want lineWidth of outer circle as 5, it is also making outer circle with as 5.
How to stop this?
My code,
<script>
function drawCircle() {
var circleCanvas = document.getElementById("myCircleCanvas");
var circle = circleCanvas.getContext("2d");
circle.beginPath();
var x = 95;
var y = 75;
var radius = 60;
circle.arc(x, y, radius, 0, 2 * Math.PI);
circle.stroke();
circle.fillStyle = '#505050';
circle.fill();
circle.fillStyle = '#fff';
var font = "bold " + radius / 3 + "px serif";
circle.font = font;
circle.fillText("AVAYA", x - x / 3, y - y / 5);
circle.fillStyle = '#000';
circle.fillText("CIE", x - x / 3 + 15, y - y / 5 + 25);
circle.arc(x, y, radius + 10, 0, 2 * Math.PI);
circle.lineWidth = 5;//just want to increase outer circle
circle.stroke(); //why drawing extra line
}
</script>
<canvas id="myCircleCanvas">Your browser does not support the HTML5 canvas tag.
</canvas>

If you want the two circles to be treated independently, close the path that you started and begin a new one before you draw the second circle. For instance:
function drawCircle() {
var circleCanvas = document.getElementById("myCircleCanvas");
var circle = circleCanvas.getContext("2d");
circle.beginPath();
var x = 95;
var y = 75;
var radius = 60;
circle.arc(x, y, radius, 0, 2 * Math.PI);
circle.stroke();
circle.fillStyle = '#505050';
circle.fill();
circle.fillStyle = '#fff';
var font = "bold " + radius / 3 + "px serif";
circle.font = font;
circle.fillText("AVAYA", x - x / 3, y - y / 5);
circle.fillStyle = '#000';
circle.fillText("CIE", x - x / 3 + 15, y - y / 5 + 25);
circle.closePath();
circle.beginPath();
circle.arc(x, y, radius + 10, 0, 2 * Math.PI);
circle.lineWidth = 5;//just want to increase outer circle
circle.stroke(); //why drawing extra line
circle.closePath();
}
http://jsfiddle.net/mudbo48j/1

Related

HTML5 Canvas rotate gradient around centre with best fit

I want to make a gradient that covers the whole canvas whatever the angle of it.
So I used a method found on a Stack Overflow post which is finally incorrect. The solution is almost right but, in fact, the canvas is not totally covered by the gradient.
It is this answer: https://stackoverflow.com/a/45628098/5594331
(You have to look at the last point named "Example of best fit.")
In my code example below, the yellow part should not be visible because it should be covered by the black and white gradient. This is mostly the code written in Blindman67's answer with some adjustments to highlight the problem.
I have drawn in green the control points of the gradient. With the right calculations, these should be stretched to the edges of the canvas at any angle.
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
function bestFitGradient(angle){
var dist = Math.sqrt(w * w + h * h) / 2; // get the diagonal length
var diagAngle = Math.asin((h / 2) / dist); // get the diagonal angle
// Do the symmetry on the angle (move to first quad
var a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
if(a1 > Math.PI){ a1 -= Math.PI }
if(a1 > Math.PI / 2 && a1 <= Math.PI){ a1 = (Math.PI / 2) - (a1 - (Math.PI / 2)) }
// get angles from center to edges for along and right of gradient
var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
var ang2 = Math.abs(diagAngle - Math.abs(a1));
// get distance from center to horizontal and vertical edges
var dist1 = Math.cos(ang1) * h;
var dist2 = Math.cos(ang2) * w;
// get the max distance
var scale = Math.max(dist2, dist1) / 2;
// get the vector to the start and end of gradient
var dx = Math.cos(angle) * scale;
var dy = Math.sin(angle) * scale;
var x0 = w / 2 + dx;
var y0 = h / 2 + dy;
var x1 = w / 2 - dx;
var y1 = h / 2 - dy;
// create the gradient
const g = ctx.createLinearGradient(x0, y0, x1, y1);
// add colours
g.addColorStop(0, "yellow");
g.addColorStop(0, "white");
g.addColorStop(.5, "black");
g.addColorStop(1, "white");
g.addColorStop(1, "yellow");
return {
g: g,
x0: x0,
y0: y0,
x1: x1,
y1: y1
};
}
function update(timer){
var r = bestFitGradient(timer / 1000);
// draw gradient
ctx.fillStyle = r.g;
ctx.fillRect(0,0,w,h);
// draw points
ctx.lineWidth = 3;
ctx.fillStyle = '#00FF00';
ctx.strokeStyle = '#FF0000';
ctx.beginPath();
ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 2px solid red;
}
<canvas id="canvas" width="300" height="200"></canvas>
In this fiddle there is a function that calculates the distance between a rotated line and a point:
function distanceToPoint(px, py, angle) {
const cx = width / 2;
const cy = height / 2;
return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}
Which is then used to find the maximum distance between the line and the corner points (only two points are considered, because the distances to the other two points are mirrored):
const dist = Math.max(
distanceToPoint(0, 0, angle),
distanceToPoint(0, height, angle)
);
Which can be used to calculate offset points for the end of the gradient:
const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
width / 2 + ox,
height / 2 + oy,
width / 2 - ox,
height / 2 - oy
)

Rotating Rectangles Around Circle Perimeter on Canvas

I'm trying to create a little circular "equalizer" effect using JavaScript and HTML canvas for a little project I'm working on, and it works great, except one little thing. It's just a series of rectangular bars moving in time to an mp3 - nothing overly fancy, but at the moment all the bars point in one direction (i.e. 0 radians, or 90 degrees).
I want each respective rectangle around the edge of the circle to point directly away from the center point, rather than to the right. I have 360 bars, so naturally, each one should be 1 degree more rotated than the previous.
I thought that doing angle = i*Math.PI/180 would fix that, but it doesn't seem to matter what I do with the rotate function - they always end up pointing in weird and wonderful directions, and being translated a million miles from where they were. And I can't see why. Can anyone see where I'm going wrong?
My frame code, for reference, is as follows:
function frames() {
// Clear the canvas and get the mp3 array
window.webkitRequestAnimationFrame(frames);
musicArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(musicArray);
ctx.clearRect(0, 0, canvas.width, canvas.height);
bars = 360;
for (var i = 0; i < bars; i++) {
// Find the rectangle's position on circle edge
distance = 100;
var angle = i * ((Math.PI * 2) / bars);
var x = Math.cos(angle) * distance + (canvas.width / 2);
var y = Math.sin(angle) * distance + (canvas.height / 2);
barWidth = 5;
barHeight = (musicArray[i] / 4);
// Fill with a blue-green gradient
var grd = ctx.createLinearGradient(x, 0, x + 40, 0);
grd.addColorStop(0, "#00CCFF");
grd.addColorStop(1, "#00FF7F");
ctx.fillStyle = grd;
// Rotate the rectangle according to position
// ctx.rotate(i*Math.PI/180); - DOESN'T WORK
// Draw the rectangle
ctx.fillRect(x, y, barHeight, barWidth);
}
For clarity I've removed part of your code. I'm using rotate as you intended. Also I'm using barHeight = (Math.random()* 50); instead your (musicArray[i]/4); because I wanted to have something to show.
Also I've changed your bars to 180. It's very probable that you won't have 360 bars but 32 or 64 or 128 or 256 . . . Now you can change the numbers of bare to one of these numbers to see the result.
I'm drawing everything around the origin of the canvas and translating the context in the center.
I hope it helps.
const canvas = document.getElementById("c");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 400;
let ch = canvas.height = 400;
let bars = 180;
let r = 100;
ctx.translate(cw / 2, ch / 2)
for (var i = 0; i < 360; i += (360 / bars)) {
// Find the rectangle's position on circle edge
var angle = i * ((Math.PI * 2) / bars);
//var x = Math.cos(angle)*r+(canvas.width/2);
//var y = Math.sin(angle)*r+(canvas.height/2);
barWidth = 2 * Math.PI * r / bars;
barHeight = (Math.random() * 50);
ctx.fillStyle = "green";
// Rotate the rectangle according to position
// ctx.rotate(i*Math.PI/180); - DOESN'T WORK
// Draw the rectangle
ctx.save();
ctx.rotate(i * Math.PI / 180);
ctx.fillRect(r, -barWidth / 2, barHeight, barWidth);
//ctx.fillRect(r ,0, barHeight, barWidth);
ctx.restore();
}
canvas {
border: 1px solid
}
<canvas id="c"></canvas>
Here is another solution, I'm preserving your initial trigonometry approach.
But instead of rectangles I used lines, I don't think it makes a difference for you, if what you need is bars moving in time to an mp3 all you need to do is change the var v = Math.random() + 1; to a reading from the Amplitude, and those bars will be dancing.
const canvas = document.getElementById("c");
canvas.width = canvas.height = 170;
const ctx = canvas.getContext("2d");
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.lineWidth = 2;
let r = 40;
let bars = 180;
function draw() {
ctx.clearRect(-100, -100, 200, 200)
for (var i = 0; i < 360; i += (360 / bars)) {
var angle = i * ((Math.PI * 2) / bars);
var x = Math.cos(angle) * r;
var y = Math.sin(angle) * r;
ctx.beginPath();
var v = Math.random() + 1;
ctx.moveTo(x, y);
ctx.lineTo(x * v, y * v)
grd = ctx.createLinearGradient(x, y, x*2, y*2);
grd.addColorStop(0, "blue");
grd.addColorStop(1, "red");
ctx.strokeStyle = grd;
ctx.stroke();
}
}
setInterval(draw, 100)
<canvas id="c"></canvas>

JS calculting width and height for rotated rectangle

am trying to draw rectangles it different angles , initially i know rectangle position data it 0 degree , then sometime i get angle greater than 0 so i have to draw on that angle , for which i then have to rotate points. so currently i can rotate points but do not know to calculate width and height after rotation.
am doing it like ...
// Main rotation function
function rotate(originX, originY,pointX, pointY, angle) {
angle = angle * Math.PI / 180.0;
return {
x: Math.cos(angle) * (pointX-originX) - Math.sin(angle) * (pointY-originY) + originX,
y: Math.sin(angle) * (pointX-originX) + Math.cos(angle) * (pointY-originY) + originY
};
}
// initial Rectangle
ctx.fillStyle = "red";
var x = 200
var y = 200
var w = 80
var h = 20
var angle = 90 ;
ctx.fillRect(x, y, w, h);
// Calculate Center
var cx = (x + (w/2));
var cy = (y + (h/2));
// highlight Center
ctx.fillStyle = "black";
ctx.fillRect(cx,cy, 5, 5);
// Rotate starting x y at angle xxx
var r = rotate(cx,cy,x,y, angle - h );
// highlight roate points
ctx.fillStyle = "yellow";
ctx.fillRect(r.x, r.y, 5, 5);
// rotate Width and Height
var r2 = rotate(cx,cy,x+w,y+h, angle - h );
// highlight roate points
ctx.fillStyle = "green";
ctx.fillRect(r2.x, r2.y, 5, 5);
ctx.save();
so it end i rotated width and height which is ok for single line , but am interested in full rotation of width of height , like it 90 angle old with will become new height and old height will become new width. so any idea how to do it
Fiddle : https://jsfiddle.net/047txgox/
fixed it , hope will help someone
var x = 40
var y = 80
var w = 80
var h = 20
var angle = 120 ;
// Calculate Center
var cx = (x + (w/2));
var cy = (y + (h/2));
context.setTransform(1,0,0,1,0,0)
context.translate(cx, cy);
context.rotate(angle*Math.PI/180);
context.translate(-cx, -cy);
context.rect(x, y, w, h);
context.fillStyle = "#FF00FF";
context.fill();
// highlight Center
context.fillStyle = "black";
context.fillRect(cx,cy, 5, 5);
context.save();
Fiddle : http://jsfiddle.net/vorjcbz7/

How can I change the fill colors of each box for a chessboard in JS, using Canvas?

I just had a quick thought in mind of drawing a chessboard using JS and Canvas, and I have this code that draws the boxes alright with for loops.
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
ctx.stroke();
ctx.closePath();
}
}
<canvas id="canvas" width="240" height="240"></canvas>
Now I'm wondering how I can access each odd box on the axes to change their fill colors (e.g. black, white, black, white, and so on).
I know using global variables isn't the best way, but this is a very small project and I just want to get some logic on how I can alternate the colors of the chessboard. Your help is very much appreciated!
You could also try only incrementing your values by 1 (instead of boxWidth), which would make it simpler to check if they are even or odd. Then you would need to either scale or multiply by boxWidth and boxHeight:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
var numbRows = Math.floor(canvas.width / boxWidth),
numbCols = Math.floor(canvas.height / boxHeight);
ctx.save();
ctx.scale(boxWidth, boxHeight);
for (x = 0; x < numbRows; x++) {
for (y = 0; y < numbCols; y++) {
if ((x+y) % 2 == 0) ctx.fillStyle = 'white';
else ctx.fillStyle = 'black';
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
ctx.stroke();
ctx.closePath();
}
}
ctx.restore();
You can use fillRect to do so like this:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.fillStyle = (x / boxWidth + y / boxHeight) % 2 === 0? "white": "black"; // determine which color to use depending on the index of x (x / boxWidth) an the index of y (y / boxHeight)
ctx.fillRect(x, y, boxWidth, boxHeight);
}
}
<canvas id="canvas" width="240" height="240"></canvas>
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var x, y,
boxWidth = 30,
boxHeight = 30;
for (x = 0; x < canvas.width; x += boxWidth) {
for (y = 0; y < canvas.height; y += boxHeight) {
ctx.beginPath();
ctx.rect(x, y, boxWidth, boxHeight);
// fill odd boxes
(x/boxWidth + y/boxHeight) % 2 && ctx.fill()
ctx.stroke();
ctx.closePath();
}
}
<canvas id="canvas" width="240" height="240"></canvas>
Alternatives to the for loop
Another way without a loop, draw the pattern 2 by 2 square in top corner then repeat that by copying the canvas onto itself.
First create the top 2 by 2 square then fill rest of board with copies.
First 2 by 2 to 4 by 2
then 4 by 2 to 8 by 2
then 8 by 2 to 8 by 4
then 8 by 4 to 8 by 8
Example
const w= 100;
canvas.height = canvas.width = w * 8;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w + w, w + w);
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, w);
ctx.fillRect(w, w, w, w);
ctx.drawImage(canvas, 0, 0, w * 2, w * 2, w * 2, y , w * 2, w * 2);
ctx.drawImage(canvas, 0, 0, w * 4, w * 2, w * 4, y , w * 4, w * 2);
ctx.drawImage(canvas, 0, 0, w * 8, w * 2, 0 , w * 2, w * 8, w * 2);
ctx.drawImage(canvas, 0, 0, w * 8, w * 4, 0 , w * 4, w * 8, w * 4);
Thus it gets drawn in 7 render calls, if the grid was larger then 2 more calls for 16 by 16, and every doubling in size only needs two more calls.
The pattern can be very complex but not create excessive render stress as in the next example that has shadows and different composite calls.
const squareSize = 72;
const boardSize = 8;
const borderSize = 8;
canvas.height = canvas.width = squareSize * boardSize + borderSize * 2;
const ctx = canvas.getContext("2d");
var x = borderSize;
var y = x;
var w = squareSize;
drawSquare(3, 3, canvas.width - 6, "black", "#F97");
drawSquare(x, y, w, "white", "#964");
drawSquare(w + x, y, w, "black", "#745");
ctx.drawImage(canvas, x, y, w, w, x + w, y + w, w, w);
ctx.drawImage(canvas, x + w, y, w, w, x, y + w, w, w);
ctx.drawImage(canvas, x, y, w * 2, w * 2, x + w * 2, y, w * 2, w * 2);
ctx.drawImage(canvas, x, y, w * 4, w * 2, x + w * 4, y, w * 4, w * 2);
ctx.drawImage(canvas, x, y, w * 8, w * 2, x, y + w * 2, w * 8, w * 2);
ctx.drawImage(canvas, x, y, w * 8, w * 4, x, y + w * 4, w * 8, w * 4);
drawSquare(0,0,canvas.width,"rgba(0,0,0,0.0)","rgba(0,0,0,0.05)");
// done.
// this function is only called twice.
function drawSquare(x,y,size,color,color2){
ctx.save();
ctx.shadowColor = color2;
ctx.shadowBlur = size * 0.2;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.rect(x,y,size,size);
ctx.clip();
ctx.lineWidth = size;
ctx.fillStyle = color;
ctx.fillRect(x,y,size,size);
ctx.globalAlpha = 0.5;
ctx.strokeRect(x - size / 2,y - size / 2, size * 2, size * 2);
ctx.shadowBlur = size * 0.5;
ctx.strokeRect(x - size / 2,y - size / 2, size * 2, size * 2);
ctx.shadowColor = "rgba(0,0,0,0)";
ctx.shadowBlur = 0;
ctx.globalAlpha = 1;
ctx.strokeStyle = color2;
ctx.lineWidth = 2;
ctx.strokeRect(x+1,y+1,size -2,size-2);
ctx.globalAlpha = 0.75;
ctx.fillRect(x+1,y+1,size-2,size-2);
ctx.globalCompositeOperation = "screen";
ctx.fillStyle = "white";
ctx.globalAlpha = 0.1;
ctx.fillRect(x,y,4,size);
ctx.fillRect(x,y,2,size);
ctx.fillRect(x+4,y,size-4,4);
ctx.fillRect(x+2,y,size-2,2);
ctx.restore();
}
canvas { border : 2px solid black; }
<canvas id="canvas" ></canvas>
Use a pattern style.
Create an offscreen canvas to hold the pattern of the top 2 by 2, draw the pattern, then assign the fillStyle of the on screen canvas to a new pattern created from the off screen canvas and fill the whole canvas.
const w = 72;
const patCan = document.createElement("canvas");
patCan.height = patCan.width = w * 2;
var ctx = patCan.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w + w, w + w);
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, w);
ctx.fillRect(w, w, w, w);
// setup display canvas
canvas.height = canvas.width = w * 8;
var ctx = canvas.getContext("2d");
ctx.fillStyle = ctx.createPattern(patCan, "repeat");
ctx.fillRect(0, 0, w * 8, w * 8);
canvas { border : 8px solid green; }
<canvas id="canvas" ></canvas>
Rendering with 3 lines of code
Here's a neat little trick you can use to draw a chess board:
var ctx = c.getContext("2d");
for(var x = 0; x < c.width; x += c.width / 4) ctx.fillRect(x, 0, c.width/8, c.height);
ctx.globalCompositeOperation = "xor"; // toggle alpha channel for every 2nd line
for(var y = 0; y < c.height; y += c.height / 4) ctx.fillRect(0, y, c.width, c.height/8);
<canvas id=c width=600 height=600></canvas>
We're using canvas' size to detemine the grid size. You can of course change this and offset to anything you like. You'd still use the divisors 4 (2 cells) and 8 (1 cell) with the actual width and height.
The first step draws vertical black stripes every other column. Then we toggle alpha channel for every other row knowing that the default color is black (rgba(0,0,0,0)) using the "xor" composite mode which toggles the alpha channel.
Just remember to start with a empty canvas (which you probably are anyways due to the need to redraw moves) and to set back composite mode to "source-over" after drawing the board.
If you'd like to change the color itself simply add an extra step at the end:
ctx.globalCompositeOperation = "source-atop"; // will draw on top of what is filled
ctx.fillStyle = "#09a";
ctx.fillRect(0, 0, c.width, c.height);
The fillStyle and fillRect() can be replaced or used with an image, pattern, gradient etc.
To fill the white background simply use composite mode "destination-over" (will draw behind anything filled using the alpha channel), then draw for background.
An alternative is to use a toggle switch when filling each cell one by one:
var ctx = c.getContext("2d");
var toggle = false;
ctx.beginPath();
for(var y=0; y < c.height; y += c.height / 8) {
toggle = !toggle; // toggle for each row so they don't line up
for(var x=0; x < c.width; x += c.width / 8) {
// toggle for each cell and check, only draw if toggle = true
if (toggle = !toggle) ctx.rect(x, y, c.width / 8, c.height / 8);
}
}
ctx.fill(); // remember to use beginPath() for consecutive draw ops
<canvas id=c width=600 height=600></canvas>
Access Logic
To know if you're inside a cell you would simply calculate the mouse position relative to the canvas (see this answer on how to do that) and then quantize (using pseudo variables here, replace with real):
var cellSize = boardWidth / 8; // assumes the board is 1:1 square
var pos = getMousePos(event); // see linked answer above
var cellX = Math.floor(pos.x / cellSize) * cellSize; // start of current cell X
var cellY = Math.floor(pos.y / cellSize) * cellSize; // start of current cell Y
(to get index of cell just drop the * cellSize part).
Example:
var ctx = c.getContext("2d"), x, y, w = c.width, h = c.height, cellSize = w / 8;
render();
ctx.lineWidth = 4; ctx.strokeStyle = "red"; ctx.setLineDash([7, 7]);
// non-optimized - in production only redraw when needed (cellX/Y changes)
c.onmousemove = function(e) {
render();
var cell = getCellPos(getMousePos(e));
if (cell.x >= 0 && cell.x < w && cell.y >=0 && cell.y < h)
ctx.strokeRect(cell.x + 2, cell.y + 2, cellSize - 4, cellSize - 4);
}
function getCellPos(pos) {
return {x: Math.floor(pos.x / cellSize) * cellSize,
y: Math.floor(pos.y / cellSize) * cellSize}
}
function getMousePos(e) {
var rect = c.getBoundingClientRect();
return {x: e.clientX-rect.x, y: e.clientY-rect.y}
}
function render() {
ctx.clearRect(0, 0, w, h);
for(x = 0; x < w; x += w>>2) ctx.fillRect(x, 0, cellSize, c.height);
ctx.globalCompositeOperation = "xor"; // toggle alpha channel for every 2nd line
for(y = 0; y < h; y += h>>2) ctx.fillRect(0, y, w, cellSize);
ctx.globalCompositeOperation = "source-atop"; // fg
ctx.fillStyle = "#3c4168";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "destination-over"; // bg
ctx.fillStyle = "#eee";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "source-over"; // reset
}
body {background:#222;margin:20px 0 0 20px;}
<canvas id=c width=600 height=600></canvas>

Rotating a line around a circle

I have two arcs with strokes and i want to have a line animate between them. The line should animate perpendicular to the points of the inner circle.
Here's something that I hacked together that is almost what I want.
things that are wrong with it are:
the length of the line is not the length between the 2 circle as it revolves around the inner circle.
sometimes the line is not perpendicular to the points of the inner circle. for example when it goes to the corner of the circle it tilts at an angle a little.
I had problem understanding how to use the trig function for lineTo. maybe because that changes the length of the line and I didn't know how to get the x and y coordinates of the outer circle to get the end point of the line. I'm used to doing math.cos * length and this gives me a line at an angle. I don't what a line I just wanted the coords of the outer circle.
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function lineAtAngle(startX, startY, angleDeg, length, startX2, startY2, angleDeg2, length2){
var angle = angleDeg * (Math.PI / 180);
var angle2 = angleDeg2 * (Math.PI / 180);
// context.moveTo(startX, startY);
context.beginPath();
context.moveTo(
Math.cos(angle) * length + startX,
Math.sin(angle) * length + startY
)
context.lineTo(
Math.cos(angle2) *(length2 )+ startX2,
Math.sin(angle2) *(length2) + startY2
)
// context.lineTo(canvas.width / 2 + 60, canvas.height / 2, angle2, length2)
context.lineWidth = 10;
context.stroke();
context.closePath();
console.log("startX2: " + startX2 + " startY2: " + startY2 )
console.log(Math.sin(angle2) + startY2)
console.log(length)
}
function myLineTo(startX, startY, angleDeg, length){
}
var length1 = canvas.width / 2 + 60 - canvas.width / 2 -30
var length2 = canvas.width / 2 ;
// var length2 = 1;
console.log(length2)
var angle1 = 0;
var angle2 = 0;
(function animate(){
context.clearRect(0,0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
context.beginPath()
context.arc(canvas.width / 2, canvas.height / 2, 30, 0, 2 * Math.PI, true)
context.lineWidth = 1;
context.stroke()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 60, 0, 2 * Math.PI, true);
context.stroke();
context.closePath()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 3, 0, 2 * Math.PI, true)
context.fill()
context.closePath();
angle1++
angle2++
// lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, length1, canvas.width / 2 + 60, canvas.height / 2, angle2, length2 )
lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, length1, canvas.width / 2 + 60, canvas.height / 2, angle2, length2 )
}())
}
canvas{
background: #aaa;
}
<canvas id="canvas" width="400" height="400"></canvas>
I believe the below is what you're trying to achieve.
I've simplified the code somewhat by using the Canvas's built-in methods to translate the coordinate space to the center and by removing the extra function which had way too many parameters passed when all it needed was one angle and two radii.
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var radius1 = 30;
var radius2 = 60;
var angle = 0;
(function animate(){
context.clearRect(0, 0, canvas.width, canvas.height);
// save state and adjust coordinate space
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.lineWidth = 1;
context.beginPath()
context.arc(0, 0, radius1, 0, 2 * Math.PI, true)
context.stroke()
context.beginPath();
context.arc(0, 0, radius2, 0, 2 * Math.PI, true);
context.stroke();
context.beginPath();
context.arc(0, 0, 3, 0, 2 * Math.PI, true);
context.fill()
++angle;
var rads = angle * Math.PI / 180;
var x = Math.cos(rads);
var y = Math.sin(rads);
context.lineWidth = 10;
context.beginPath();
context.moveTo(radius1 * x, radius1 * y);
context.lineTo(radius2 * x, radius2 * y);
context.stroke();
// restore transformations for next pass
context.restore();
window.requestAnimationFrame(animate)
}())
}
canvas { background: #aaa; }
<canvas id="canvas" width="400" height="200"></canvas>
The idea for drawing a line is that you need to supply a start point (moveTo), and an end point (lineTo). Your current code is complicating the whole thing with multiple angles and lengths. What you want to imagine you are doing is starting at the center of your circle, then adding an offset which places the start point at the edge of the inner circle. Using trig is perfectly fine here. The length of your line will simply be outer radius minus inner radius, in the same direction as your offset (only one angle was needed).
Without fundamentally changing your approach, the code below shows the changes you could use to try this. Have your line function take a starting point (x, y) and an offset, as well as the angle and length. The starting point is your circle center, and the offset is the radius of the inner circle. The length (again) is simply the outer radius minus the inner radius.
window.onload = function(){
var innerCircleRadius = 30;
var outerCircleRadius = 60;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var angle1 = 0;
function lineAtAngle(startX, startY, angleDeg, offset, length) {
var angle = angleDeg * (Math.PI / 180); // Convert to radians.
var cosAngle = Math.cos(angle); // Only need cos(angle) once.
var sinAngle = Math.sin(angle); // Only need sin(angle) once.
var startXPos = cosAngle * offset + startX;
var startYPos = sinAngle * offset + startY;
var endXPos = cosAngle * length + startXPos;
var endYPos = sinAngle * length + startYPos;
context.beginPath();
context.moveTo(startXPos, startYPos);
context.lineTo(endXPos, endYPos);
context.lineWidth = 10;
context.stroke();
context.closePath();
}
(function animate() {
context.clearRect(0,0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
context.beginPath()
context.arc(canvas.width / 2, canvas.height / 2, innerCircleRadius, 0, 2 * Math.PI, true)
context.lineWidth = 1;
context.stroke()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, outerCircleRadius, 0, 2 * Math.PI, true);
context.stroke();
context.closePath()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 3, 0, 2 * Math.PI, true)
context.fill()
context.closePath();
angle1++
lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, innerCircleRadius, outerCircleRadius - innerCircleRadius);
}())
}
canvas {
background: #aaa;
}
<canvas id="canvas" width="400" height="400"></canvas>

Categories