How to rotate a canvas circle continuously? - javascript

https://codepen.io/saketkr7/pen/KKmzXOV
I have a circle canvas in which n items are placed, I want to rotate all items placed? How can I do that?
[![`
const canvas = document.getElementById('myCanvas');
var items = ['a', 'b' , 'c', 'd', 'e', 'g'];
const ctx = canvas.getContext('2d');
var n = 6;
var numElements = 8;
var angle = 0;
var step = (2*Math.PI) / numElements;
var rotateAngle = 36 * Math.PI / 180;
for(var i = 0; i < numElements; i++) {
var x = 500/2 + 100 * Math.cos(angle);
var y = 500/2 + 100 * Math.sin(angle);
console.log(x, y);
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.stroke();
angle += step;
}
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="500" height="500">
Your browser does not support the HTML canvas tag.
</canvas>
</body>
</html>
`]1]1

requestAnimationFrame
Use requestAnimationFrame(callback) (rAF) to render the animation.
The callback function is responsible for rendering each animation frame. In this case it will clear the canvas and draw the circles.
The callback function gets the time in milliseconds (1/1000th seconds). You can use that to set the rotation angle. The first example uses the time and the constant rate to define the number of rotations per second.
In the callback function you need to request the new frame by calling rAF.
To start the animation request the first frame.
Rendering
Modify your code so that it is a function that can be called for each frame of the animation. In the example your modified code is in the function drawCircles(angle) where angle is the current rotation in radians.
Pass it an argument that is the current rotation.
Example
The snippet below does what is described above.
const ctx = canvas.getContext('2d');
const rate = 0.2; // Number of rotations per second
function drawCircles(angle) {
var i = 0;
const numElements = 8;
const step = (2 * Math.PI) / numElements;
ctx.beginPath();
while (i < numElements) {
const x = ctx.canvas.width / 2 + 100 * Math.cos(angle + i * step);
const y = ctx.canvas.height / 2 + 100 * Math.sin(angle + i * step);
ctx.moveTo(x + 10, y);
ctx.arc(x, y, 10, 0, 2 * Math.PI);
i++;
}
ctx.stroke();
}
requestAnimationFrame(renderLoop); // rAF to start animation
function renderLoop(time) { // rAF callback
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawCircles(((time * Math.PI * 2) / 1000) * rate);
requestAnimationFrame(renderLoop); // request next frame
}
<canvas id = "canvas" width="220" height="220"></canvas>
On many devices the frame rate will be very stable, you can use a fixed rate time to get a smoother animation. As shown in next snippet. Be aware that time will drift if device drops frames.
const ctx = canvas.getContext('2d');
const rate = 0.2; // APPROX Number of rotations per second
var frame = 0; // counts frames
function drawCircles(angle) {
var i = 0;
const numElements = 8;
const step = (2 * Math.PI) / numElements;
ctx.beginPath();
while (i < numElements) {
const x = ctx.canvas.width / 2 + 100 * Math.cos(angle + i * step);
const y = ctx.canvas.height / 2 + 100 * Math.sin(angle + i * step);
ctx.moveTo(x + 10, y);
ctx.arc(x, y, 10, 0, 2 * Math.PI);
i++;
}
ctx.stroke();
}
requestAnimationFrame(renderLoop); // rAF to start animation
function renderLoop() { // rAF callback
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
time = frame++ * (1000 / 60); // Assumes 60 fps
drawCircles(((time * Math.PI * 2) / 1000) * rate);
requestAnimationFrame(renderLoop); // request next frame
}
<canvas id = "canvas" width="220" height="220"></canvas>
Update
Re comments
The #MDN.API.CanvasRenderingContext2D# is not best for 3D. The best option is to use WebGL However there is a steep learning curve for WebGL.
3D can be done on the 2D API but you need to implement all the 3D rendering code in JS which will be orders of magnitude slower than WebGl.
3D via 2D API
The example below uses the 2D canvas to render a rotating ring of toon-shaded spheres rotating in 3D.
It is the most basic example and will not support cameras, lights, textures, etc..
const ctx = canvas.getContext('2d');
const rate = 0.2; // APPROX Number of rotations per second
const numCircles = 18;
const perspectiveRange = 300; // dist between front and back planes
const ringRadius = 60; // in pixels
const circleRadius = 10; // in pixels. Radius of circle at z === 0
const colors = [["#B11", "#F22"], ["#DB0", "#FF0"]];
var frame = 0; // counts frames
function drawCircles(angle, rotY) { // rotZ rotates around Y axis (in and out of screen)
var i = 0;
ctx.fillStyle = "#FF0";
const step = (2 * Math.PI) / numCircles;
const circles = [];
// The transform for y rotation
const dx = Math.cos(rotY);
const dy = Math.sin(rotY);
// get 3D location of each circle
while (i < numCircles) {
const x = ringRadius * Math.cos(angle + i * step);
const y = ringRadius * Math.sin(angle + i * step);
circles.push({x: x * dx, y, z: x * dy, colIdx: i % 2});
i++;
}
// sort circles from back to front
circles.sort((a, b) => b.z - a.z);
// center on canvas
ctx.setTransform(1,0,0,1, ctx.canvas.width / 2, ctx.canvas.height / 2);
// draw 3D circles with perspective
for (const c of circles) {
const col = colors[c.colIdx];
// Calculate perspective scale. The further from the front the
// smaller the perspective scale
const p = (perspectiveRange - c.z) / perspectiveRange;
// Scale radius, x, y pos and line with by perspective scale
const r = Math.abs(p * circleRadius);
const x = p * c.x;
const y = p * c.y;
ctx.lineWidth = 1.5 * p;
// shaded color
ctx.fillStyle = col[0];
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
// highlight color
ctx.fillStyle = col[1];
ctx.beginPath();
ctx.arc(x - r * 0.1, y - r * 0.1, r * 0.8, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = "#FFFA";
ctx.beginPath();
ctx.arc(x - r * 0.3, y - r * 0.3, r * 0.3, 0, 2 * Math.PI);
ctx.fill();
}
// reset canvas transform
ctx.setTransform(1,0,0,1,0, 0);
}
requestAnimationFrame(renderLoop); // rAF to start animation
function renderLoop() { // rAF callback
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
time = frame++ * (1000 / 60); // Assumes 60 fps
const ang = ((time * Math.PI * 2) / 1000) * rate
drawCircles(ang, ang / 2);
requestAnimationFrame(renderLoop); // request next frame
}
<canvas id = "canvas" width="180" height="180"></canvas>

Related

HTML5 Canvas sub-pixel position rendering issue

I'm trying to draw multiple rectangles positioned very densely in a straight line so they form a single shape like so:
const canvas = document.getElementById("c");
const ctx = canvas.getContext("2d");
const a = 87;
const l = 300;
const col = 'rgba(0,0,64)';
const q = 4; // density - change
ctx.moveTo(300, 100);
ctx.lineTo(
300 + (Math.cos(degtorad(a)) * l),
100 + (Math.sin(degtorad(a)) * l)
);
ctx.strokeStyle = col;
ctx.lineWidth = 3;
ctx.stroke();
stack(l, a, col);
function stack(length, angle, color) {
ctx.fillStyle = color;
for (let i = 1; i < length * q; i++) {
let x = 100 + (Math.cos(degtorad(angle)) * i / q);
let y = 100 + (Math.sin(degtorad(angle)) * i / q);
console.log(`x:${x}, y:${y}`);
ctx.beginPath();
ctx.rect(x, y, 150, 100);
ctx.fill();
}
}
function degtorad(angle) {
return angle * (Math.PI / 180.0);
}
https://jsfiddle.net/fallenartist/qeo7a1gx/
However, I don't like the jagged edge at all! It looks like there are issues with canvas rendering at sub-pixel coordinates. Notice how rendering of edges of absolutely positioned rectangles differ from a straight line:
Can anyone explain why is this? And how it can be improved?

How do I slow an animation down in canvas-sketch?

In line 6, you'll see that I have animate as true. I tried adding a duration and lowering the fps, but this doesn't impact the movement speed. I've come to the conclusion that the duration and fps don't impact the animation speed as it doesn't impact other canvas sketch animations I've worked on. I could be wrong.
Thanks for checking out this post.
const canvasSketch = require('canvas-sketch');
const math = require('canvas-sketch-util/math');
const random = require('canvas-sketch-util/random');
const settings = {
dimensions: [ 1080, 1080 ],
animate: true, // <––––––––––––––––––––– line 6
duration: 3, // <–––––––––––––––––––––
fps: 5, // <–––––––––––––––––––––
};
const sketch = () => {
return ({ context, width, height }) => {
context.fillStyle = 'white';
context.fillRect(0, 0, width, height);
// Gradient
let gradient = context.createLinearGradient(0, 0, width * 1.2, 0);
gradient.addColorStop("0", "gold");
gradient.addColorStop("0.03" ,"mediumblue");
gradient.addColorStop("0.99" ,"gold");
context.fillStyle = gradient;
context.strokeStyle = gradient;
const cx = width * 0.5;
const cy = height * 0.5;
const w = width * 0.012;
const h = height * 0.12;
let x, y;
const num = 60;
const radius = width * 0.36
for (let i = 0; i < num; i++) {
const slice = math.degToRad(360/ num);
const angle = slice * i;
x = cx + radius * Math.sin(angle);
y = cy + radius * Math.cos(angle);
// Scale of strokes
context.save();
context.translate(x, y);
context.rotate(-angle);
context.scale(random.range(0.1, 2.1), random.range(0.12, 1.2));
// Placement of strokes
context.beginPath();
context.rect(-w * 2.7, random.range(3, -h * 1.5), w, h);
context.fill();
context.restore();
context.save();
context.translate(cx, cy);
context.rotate(-angle);
// Arc thickness
context.lineWidth = random.range(5, 20);
// Arc/slice width
context.beginPath();
// Arc distance from the center, arc rotation length
context.arc(0, 0, radius * random.range(0.6, 1.2), slice * random.range(0.6, -33), slice * random.range(1.2,45));
context.stroke();
context.restore();
}
};
};
canvasSketch(sketch, settings);

How can I calculate points on a circular path? (Center known)

I'm currently working on a small HTML canvas game (zero point of the canvas top-left). I need the coordinates (X,Y) on a circle.
The radius and the center are known.
My variables:
var radius = 50;
var center_x = 200;
var center_y = 200;
var angle = 45;
The formula for a point on a circle, given the angle, is:
x = xcenter + r·cos(𝛼)
y = ycenter + r·sin(𝛼)
...where 𝛼 is the angle in radians.
Since on a web page the Y coordinate is downwards, you would subtract the term in the formula.
Here is a demo, where the angle changes continually:
var radius = 50;
var center_x = 100;
var center_y = 100;
var angle = 50; // Will change in this demo
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const span = document.querySelector("span");
const loop = () => {
angle = (angle + 1) % 360;
// Formula:
var rad = angle * Math.PI / 180;
var x = center_x + radius * Math.cos(rad);
var y = center_y - radius * Math.sin(rad);
// Draw point
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ff2626";
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2, true);
ctx.fill();
// Display angle
span.textContent = angle;
// Repeat at next paint cycle
requestAnimationFrame(loop);
};
loop();
<div>Angle: <span></span></div>
<canvas width=500 height=160></canvas>

Simplest way to plot points randomly inside a circle

I have a basic JSFiddle whereby I want to have random points plotted inside a circle.
But I do not know how to limit the points to be inside the circle.
This is what I currently have:
var ctx = canvas.getContext('2d'),
count = 1000, // number of random points
cx = 150,
cy = 150,
radius = 148;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(canvas.width/2, canvas.height/2, radius, 0, 2*Math.PI);
ctx.closePath();
ctx.fillStyle = '#00000';
ctx.fill();
// create random points
ctx.fillStyle = '#ffffff';
while(count) {
// randomise x:y
ctx.fillRect(x + canvas.width/2, y + canvas.height/2, 2, 2);
count--;
}
How would i go about generating random (x,y) coordinates to plot random points inside the circle?
My current fiddle: http://jsfiddle.net/e8jqdxp3/
To plot points randomly in a circle, you can pick a random value from the radius squared, then square root it, pick a random angle, and convert the polar coordinate to rectangular. The square / square root step ensures that we get a uniform distribution (otherwise most points would be near the center of the circle).
So the formula to plot a random point in the circle is the following, where r' is a random value between 0 and r2, and θ is a random value between 0 and 2π:
Screenshot of result:
Live Demo:
var canvas = document.getElementById("thecanvas");
var ctx = canvas.getContext('2d'),
count = 1000, // number of random points
cx = 150,
cy = 150,
radius = 148;
ctx.fillStyle = '#CCCCCC';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
// create random points
ctx.fillStyle = '#ffffff';
while (count) {
var pt_angle = Math.random() * 2 * Math.PI;
var pt_radius_sq = Math.random() * radius * radius;
var pt_x = Math.sqrt(pt_radius_sq) * Math.cos(pt_angle);
var pt_y = Math.sqrt(pt_radius_sq) * Math.sin(pt_angle);
ctx.fillRect(pt_x + canvas.width / 2, pt_y + canvas.width / 2, 2, 2);
count--;
}
<canvas id="thecanvas" width="400" height="400"></canvas>
JSFiddle Version: https://jsfiddle.net/qc735bqw/
Randomly pick dSquared (0..radius^2) and theta (0..2pi), then
x = sqrt(dSquared) cos(theta)
y = sqrt(dSquared) sin(theta)
JSFiddle
var ctx = canvas.getContext('2d'),
count = 1000, // number of random points
cx = canvas.width/2,
cy = canvas.height/2,
radius = 148;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(0+canvas.width/2, 0+canvas.height/2, radius, 0, 2*Math.PI);
ctx.closePath();
ctx.fillStyle = '#00000';
ctx.fill();
ctx.fillStyle = '#ffffff';
while(count) {
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height;
var xDiff = cx - x;
var yDiff = cy - y;
if(Math.sqrt(xDiff*xDiff+yDiff*yDiff)<radius)
{
ctx.fillRect(x, y, 2, 2);
count--;
}
}
This worked for me:
const getRandomCoordinateInCircle = radius => {
var angle = Math.random() * Math.PI * 2;
const x = Math.cos(angle) * radius * Math.random();
const y = Math.sin(angle) * radius * Math.random();
return { x, y };
};
console.log(getRandomCoordinateInCircle(1000);
// { x: 118.35662725763385, y: -52.60516556856313 }
Returns a random point with { x: 0, y: 0} as the centre of the circle.

Create animation with circles time dependent

Hi I try to make a animation. One of the 3 circles which become drawed when the function is called should move from right to left at first one random (yellow, blue or orange) circle should become drawed on the canvas then after 3 seconds the next random circle and then after 2,8 seconds and so far.
How can I do that? Now the circles become drawed every time again when the mainloop starts run again.
window.onload = window.onresize = function() {
var C = 1; // canvas width to viewport width ratio
var el = document.getElementById("myCanvas");
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var canvasWidth = viewportWidth * C;
var canvasHeight = viewportHeight;
el.style.position = "fixed";
el.setAttribute("width", canvasWidth);
el.setAttribute("height", canvasHeight);
var x = canvasWidth / 100;
var y = canvasHeight / 100;
var ballx = canvasWidth / 100;
var n;
window.ctx = el.getContext("2d");
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// draw triangles
function init() {
ballx;
return setInterval(main_loop, 1000);
}
function drawcircle1()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
}
function drawcircle2()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'blue';
ctx.fill();
}
function drawcircle3()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 105, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'orange';
ctx.fill();
}
function draw() {
var counterClockwise = false;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//first halfarc
ctx.beginPath();
ctx.arc(x * 80, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
//second halfarc
ctx.beginPath();
ctx.arc(x * 50, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
//third halfarc
ctx.beginPath();
ctx.arc(x * 20, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw stop button
ctx.beginPath();
ctx.moveTo(x * 87, y * 2);
ctx.lineTo(x * 87, y * 10);
ctx.lineWidth = x;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * 95, y * 2);
ctx.lineTo(x * 95, y * 10);
ctx.lineWidth = x;
ctx.stroke();
function drawRandom(drawFunctions){
//generate a random index
var randomIndex = Math.floor(Math.random() * drawFunctions.length);
//call the function
drawFunctions[randomIndex]();
}
drawRandom([drawcircle1, drawcircle2, drawcircle3]);
}
function update() {
ballx -= 0.1;
if (ballx < 0) {
ballx = -radius;
}
}
function main_loop() {
draw();
update();
collisiondetection();
}
init();
function initi() {
console.log('init');
// Get a reference to our touch-sensitive element
var touchzone = document.getElementById("myCanvas");
// Add an event handler for the touchstart event
touchzone.addEventListener("mousedown", touchHandler, false);
}
function touchHandler(event) {
// Get a reference to our coordinates div
var can = document.getElementById("myCanvas");
// Write the coordinates of the touch to the div
if (event.pageX < x * 50 && event.pageY > y * 10) {
ballx += 1;
} else if (event.pageX > x * 50 && event.pageY > y * 10 ) {
ballx -= 1;
}
console.log(event, x, ballx);
draw();
}
initi();
draw();
}
I'm a bit confused by your code, but I think I understand that you want to know how to delay when each circle will start animating to the left.
Here's how to animate your yellow, blue & orange circles with different delays:
Define the 3 circles using javascript objects and store all definintions in an array.
Inside an animation loop:
Calculate how much time has elapsed since the animation began
Loop through each circle in the array
If a circle's delay time as elapsed, animate it leftward
When all 3 circles have moved offscreen-left, stop the animation loop.
Here's annotated code and a Demo:
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasWidth=canvas.width;
var canvasHeight=canvas.height;
// predifine PI*2 because it's used often
var PI2=Math.PI*2;
// startTime is used to calculate elapsed time
var startTime;
// define 3 circles in javascript objects and put
// them in the arcs[] array
var arcs=[];
addArc(canvasWidth,canvasHeight/2,20,0,PI2,0,-1,'yellow');
addArc(canvasWidth,canvasHeight/2+40,20,0,PI2,3000,-1,'blue');
addArc(canvasWidth,canvasHeight/2+80,20,0,PI2,8000,-1,'orange');
// begin animating
requestAnimationFrame(animate);
function animate(time){
// set startTime if it isn't already set
if(!startTime){startTime=time;}
// calc elapsedTime
var elapsedTime=time-startTime;
// clear the canvas
ctx.clearRect(0,0,canvasWidth,canvasHeight);
// assume no further animating is necessary
// The for-loop may change the assumption
var continueAnimating=false;
for(var i=0;i<arcs.length;i++){
var arc=arcs[i];
// update this circle & report if it wasMoved
var wasMoved=update(arc,elapsedTime);
// if it wasMoved, then change assumption to continueAnimating
if(wasMoved){continueAnimating=true;}
// draw this arc at its current position
drawArc(arc);
}
// if update() reported that it moved something
// then request another animation loop
if(continueAnimating){
requestAnimationFrame(animate);
}else{
// otherwise report the animation is complete
alert('Animation is complete');
}
}
function update(arc,elapsedTime){
// has this arc's animation delay been reached by elapsedTime
if(elapsedTime>=arc.delay){
// is this arc still visible on the canvas
if(arc.cx>-arc.radius){
// if yes+yes, move this arc by the specified moveX
arc.cx+=arc.moveX;
// report that we moved this arc
return(true);
}
}
// report that we didn't move this arc
return(false);
}
// create a javascript object defining this arc
function addArc(cx,cy,radius,startAngle,endAngle,
animationDelay,moveByX,color){
arcs.push({
cx:cx,
cy:cy,
radius:radius,
start:startAngle,
end:endAngle,
// this "delay" property is what causes this
// circle to delay before it starts to animate
delay:animationDelay,
moveX:moveByX,
color:color,
});
}
// draw a given arc
function drawArc(a){
ctx.beginPath();
ctx.arc(a.cx,a.cy,a.radius,a.start,a.end);
ctx.fillStyle=a.color;
ctx.fill();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=400 height=300></canvas>

Categories

Resources