Related
I have written a sine graph plotting program. It works apart from the fact that the graph is choppy. I need to make it so that I can stretch the line without changing the x values of the points.
Here is the project: https://editor.p5js.org/korczaka6052/sketches/49-sJW6wz
let slidery;
let sliderx
width_ = 800
height_ = 400
linelen = width_ - 100
function setup() {
createCanvas(width_, height_);
//create sliders controlling xstep and ystep
slidery = createSlider(0, 100);
slidery.position(10, 10);
slidery.style('width', '200px');
sliderx = createSlider(0, 100);
sliderx.position(250, 10);
sliderx.style('width', '200px');
}
class createPoint { //create a point with its own x and y coordinate, ik could use vectors
constructor(x_, y_) {
this.x = 50 + x_;
this.y = height_ / 2 - y_;
}
show() {
fill(0)
circle(this.x, this.y, 1)
}
writetext() {
textSize(10)
text(this.x, this.x, height / 2 + 10)
}
}
//where all the points will be stored
points = [];
xstep = 1;
ystep = 50;
looper = 0;
function draw() {
//set xstep and ystep to their slider values
ystep = slidery.value()
xstep = sliderx.value()
stroke(0)
background(220);
//create graph lines
line(50, height / 2, width - 50, height / 2);
line(50, 50, 50, height - 50);
//for every [ystep] pixels calculate y based off equation
for (i = 0; i < 800; i++) {
points[i] = new createPoint(i * xstep, sin(i) * ystep); //creates that point as object with x and y value, y = sin(x)
}
//create line between current and previous point
for (i = 1; i < 800; i++) {
stroke(255, 0, 0)
//create only if point is inside canvas
if (points[i - 1].y < height) {
line(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y)
}
}
//create white borders to cut off extreme points
noStroke(0)
fill(220)
rect(width - 50, 0, width, height)
rect(width, height, width - 50, 0)
rect(0, 0, width, 50)
rect(0, height, width, -50)
looper++
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I am relatively new to p5.js so please excuse bad code.
The reason your line is choppy is because when xstep is greater than 1, the horizontal distance between each pair of points is relatively large. When you start out with an xstep of 50 you may have 800 points but only the first 16 or so are actually on the screen. Instead of advancing your x coordinate by xstep you should just advance by 1 (i.e., use i for the first argument to createPoint instead of i * xstep). When you do so, in order to draw the desired curve, you need to change the input to the sin() function used to calculate the corresponding y value. Because when you move forward by one pixel you are only moving to the right by 1 / xstep in the graph coordinate system, you simply need to use sin(i / xstep) instead of sin(i).
let slidery;
let sliderx;
let smooothCheckbox;
const width_ = 800;
const height_ = 400;
const linelen = width_ - 100;
function setup() {
createCanvas(width_, height_);
//create sliders controlling xstep and ystep
slidery = createSlider(0, 100);
slidery.position(10, 10);
slidery.style('width', '200px');
sliderx = createSlider(0, 100);
sliderx.position(250, 10);
sliderx.style('width', '200px');
smoothCheckbox = createCheckbox('Smooth?', true);
smoothCheckbox.position(10, 30);
}
class createPoint { //create a point with its own x and y coordinate, ik could use vectors
constructor(x_, y_) {
this.x = 50 + x_;
this.y = height_ / 2 - y_;
}
show() {
fill(0)
circle(this.x, this.y, 1)
}
writetext() {
textSize(10)
text(this.x, this.x, height / 2 + 10)
}
}
//where all the points will be stored
let points = [];
let xstep = 1;
let ystep = 50;
let looper = 0;
function draw() {
//set xstep and ystep to their slider values
ystep = slidery.value();
xstep = sliderx.value();
stroke(0);
background(220);
//create graph lines
line(50, height / 2, width - 50, height / 2);
line(50, 50, 50, height - 50);
//for every [xstep] pixels calculate y based off equation
for (i = 0; i < 800; i++) {
if (smoothCheckbox.checked()) {
points[i] = new createPoint(i, sin(i / xstep) * ystep); //creates that point as object with x and y value, y = sin(x)
} else {
points[i] = new createPoint(i * xstep, sin(i) * ystep);
}
}
//create line between current and previous point
for (i = 1; i < 800; i++) {
stroke(255, 0, 0);
//create only if point is inside canvas
if (points[i - 1].y < height) {
line(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y);
}
}
//create white borders to cut off extreme points
noStroke(0);
fill(220);
rect(width - 50, 0, width, height);
rect(width, height, width - 50, 0);
rect(0, 0, width, 50);
rect(0, height, width, -50);
looper++;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I have written this code to demonstrate a basic visual p5js project. In here there are 10 balls of varying sizes and colors that spawn at random positions, move around in the canvas and might collide with each other. I am not looking for elastic collision or "realistic" collision physics for that matter. I just want the balls to change to a different direction (can be random as long as it works) and work accordingly.
Here is my code :
class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}
//collision detection
collide(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
if (dist < 0) { //HELP HERE! <--
this.vel.mult(-1);
check.vel.mult(-1);
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(window.windowWidth, window.windowHeight);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
balls.push(
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 50),
color(random(255), random(255), random(255))
)
);
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}
Here is a link to the project : https://editor.p5js.org/AdilBub/sketches/TNn2OREsN
All I need is the collision to change the direction of the ball to a random direction and not get stuck. Any help would be appreciated. I am teaching kids this program so I just want basic collision, doesnot have to be "realistic".
Any help is appreciated. Thank you.
The issues you are currently encountering with balls being stuck has to do with randomly generating balls that overlap such that after one iteration of movement they still overlap. When this happens both balls will simply oscillate in place repeatedly colliding with each other. You can prevent this simply by checking for collisions before adding new balls:
class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}
isColliding(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
return dist < 0;
}
//collision detection
collide(check) {
if (this.isColliding(check)) {
this.vel.x *= -1;
this.vel.y *= -1;
check.vel.x *= -1;
check.vel.y *= -1;
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(500, 500);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
let newBall =
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 40),
color(random(255), random(255), random(255))
);
let isOk = true;
// check for collisions with existing balls
for (let j = 0; j < balls.length; j++) {
if (newBall.isColliding(balls[j])) {
isOk = false;
break;
}
}
if (isOk) {
balls.push(newBall);
} else {
// try again
i--;
}
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
That said, fully elastic collisions (which means collisions are instantaneous and involve no loss of energy due to deformation and resulting heat emission) are actually quite simple to simulate. Here's a tutorial I made on OpenProcessing demonstrating the necessary concepts using p5.js: Elastic Ball Collision Tutorial.
Here's the final version of the code from that tutorial:
const radius = 30;
const speed = 100;
let time;
let balls = []
let boundary = [];
let obstacles = [];
let paused = false;
function setup() {
createCanvas(400, 400);
angleMode(DEGREES);
ellipseMode(RADIUS);
boundary.push(createVector(60, 4));
boundary.push(createVector(width - 4, 60));
boundary.push(createVector(width - 60, height - 4));
boundary.push(createVector(4, height - 60));
obstacles.push(createVector(width / 2, height / 2));
balls.push({
pos: createVector(width * 0.25, height * 0.25),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.75, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.25, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
time = millis();
}
function keyPressed() {
if (key === "p") {
paused = !paused;
time = millis();
}
}
function draw() {
if (paused) {
return;
}
deltaT = millis() - time;
time = millis();
background('dimgray');
push();
fill('lightgray');
stroke('black');
strokeWeight(2);
beginShape();
for (let v of boundary) {
vertex(v.x, v.y);
}
endShape(CLOSE);
pop();
push();
fill('dimgray');
for (let obstacle of obstacles) {
circle(obstacle.x, obstacle.y, radius);
}
pop();
for (let i = 0; i < balls.length; i++) {
let ball = balls[i];
// update position
ball.pos = createVector(
min(max(0, ball.pos.x + ball.vel.x * (deltaT / 1000)), width),
min(max(0, ball.pos.y + ball.vel.y * (deltaT / 1000)), height)
);
// check for collisions
for (let i = 0; i < boundary.length; i++) {
checkCollision(ball, boundary[i], boundary[(i + 1) % boundary.length]);
}
for (let obstacle of obstacles) {
// Find the tangent plane that is perpendicular to a line from the obstacle to
// the moving circle
// A vector pointing in the direction of the moving object
let dirVector = p5.Vector.sub(ball.pos, obstacle).normalize().mult(radius);
// The point on the perimiter of the obstacle that is in the direction of the
// moving object
let p1 = p5.Vector.add(obstacle, dirVector);
checkCollision(ball, p1, p5.Vector.add(p1, p5.Vector.rotate(dirVector, -90)));
}
// Check for collisions with other balls
for (let j = 0; j < i; j++) {
let other = balls[j];
let distance = dist(ball.pos.x, ball.pos.y, other.pos.x, other.pos.y);
if (distance / 2 < radius) {
push();
let midPoint = p5.Vector.add(ball.pos, other.pos).div(2);
let boundaryVector = p5.Vector.sub(other.pos, ball.pos).rotate(-90);
let v1Parallel = project(ball.vel, boundaryVector);
let v2Parallel = project(other.vel, boundaryVector);
let v1Perpendicular = p5.Vector.sub(ball.vel, v1Parallel);
let v2Perpendicular = p5.Vector.sub(other.vel, v2Parallel);
ball.vel = p5.Vector.add(v1Parallel, v2Perpendicular);
other.vel = p5.Vector.add(v2Parallel, v1Perpendicular);
let bounce = min(radius, 2 * radius - distance);
ball.pos.add(p5.Vector.rotate(boundaryVector, -90).normalize().mult(bounce));
other.pos.add(p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce));
pop();
}
}
}
// Only draw balls after all position updates are complete
for (let ball of balls) {
circle(ball.pos.x, ball.pos.y, radius);
}
}
function drawLine(origin, offset) {
line(origin.x, origin.y, origin.x + offset.x, origin.y + offset.y);
}
// Handles collision with a plane given two points on the plane.
// It is assumed that given a vector from p1 to p2, roating that vector
// clockwise 90 degrees will give a vector pointing to the in-bounds side of the
// plane (i.e. a "normal").
function checkCollision(ball, p1, p2) {
let boundaryVector = p5.Vector.sub(p2, p1);
let objVector = p5.Vector.sub(ball.pos, p1);
let angle = boundaryVector.angleBetween(objVector);
let distance = objVector.mag() * sin(angle);
if (distance <= radius) {
// Collision
let vParallel = project(ball.vel, boundaryVector);
let vPerpendicular = p5.Vector.sub(ball.vel, vParallel);
ball.vel = p5.Vector.add(vParallel, p5.Vector.mult(vPerpendicular, -1));
let bounce = min(radius, (radius - distance) * 2);
// If the ball has crossed over beyond the plane we want to offset it to be on
// the in-bounds side of the plane.
let bounceOffset = p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce);
ball.pos.add(bounceOffset);
}
}
// p5.Vector helpers
function project(vect1, vect2) {
vect2 = p5.Vector.normalize(vect2);
return p5.Vector.mult(vect2, p5.Vector.dot(vect1, vect2));
}
function reject(vect1, vect2) {
return p5.Vector.sub(vect1, project(vect1, vect2));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I'm trying to get a red and blue circle spinning in P5 using some maths. It's comprised of two semi-circles. As you can see the two circles are currently orbiting a 10 pixel radius, rather than spinning. If I change the radius to 0, it loses that relationship. I don't want to use the rotate function...
let angle = 0; //declare a variable for the initial angle
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
background(255);
noStroke;
angleMode(DEGREES);
ellipseMode(CENTER);
let posX = 200; //change the x axis
let posY = 200; //change the y axis
let reSize = 200; //change the size
let rotationSpeed = 1; //change the rotation speed
let radius = 10;
let x = radius * cos(angle);
let y = radius * sin(angle);
fill('red');
arc(posX+x, posY+y, reSize, reSize, 0, 180);
fill('blue');
arc(posX+x, posY+y, reSize, reSize, 180, 360);
angle += rotationSpeed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
let angle = 0; //declare a variable for the initial angle
let currentAnglePercentage = 0;
function getCurrentAngle() {
return map(currentAnglePercentage % 100, 0, 100, 0, 360);
}
function setup() {
createCanvas(400, 400);
noStroke();
}
function draw() {
currentAnglePercentage++;
background(255);
noStroke;
angleMode(DEGREES);
ellipseMode(CENTER);
let posX = 200; //change the x axis
let posY = 200; //change the y axis
let reSize = 200; //change the size
let rotationSpeed = 1; //change the rotation speed
let radius = 10;
let x = radius * cos(angle);
let y = radius * sin(angle);
const c1a1 = 0 + getCurrentAngle();
const c1a2 = 180 + getCurrentAngle();
const c2a1 = 180 + getCurrentAngle();
const c2a2 = 360 + getCurrentAngle();
fill('red');
arc(posX + x, posY + y, reSize, reSize, c1a1, c1a2);
fill('blue');
arc(posX + x, posY + y, reSize, reSize, c2a1, c2a2);
angle += rotationSpeed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
you could change the angle when you draw the arc.
I need to draw multiples balls inside a rect. I have a rect and 4 informations. Width and height of the rect.. numbers of balls per line and numbers of lines. That's been said I have to draw, for example, 4 balls at the same line. starting by the corners(That I was able to do) but I can't figure out how to draw more than 2 balls, example: If I have 3 balls, I need to draw 2 in the corners and 1 in the middle, if I have 4 balls... 2 in the corners and 2 in the middle. I had the idea of think about the rect as a matrix but having no luck.. link to see what I mean
If you need to drawn for example n dragon balls on line then you can divide length with n + 1 to get spacing between center of balls, or if you want different offset on start and end then you would divide (width - 2*offset) / (n - 1).
<canvas id="canvas" width="300" height="100">
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
class Rect {
constructor(x, y, width, heght) {
this.x = x;
this.y = y;
this.width = width;
this.heght = heght;
}
}
class Circle {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
class Scene
{
constructor() {
this.items = [];
}
clear() {
this.items = [];
}
add(item) {
this.items.push(item);
}
draw(ctx) {
for(let item of this.items) {
if (item instanceof Rect) {
ctx.beginPath();
ctx.rect(item.x, item.y, item.width, item.heght);
ctx.stroke();
} else if (item instanceof Circle) {
ctx.beginPath();
ctx.arc(item.x, item.y, item.radius, 0, 2 * Math.PI);
ctx.stroke();
}
}
}
}
const scene = new Scene();
scene.clear();
scene.add(new Rect(0, 0, 300, 100));
let n = 5;
let offset = 30;
let spacing = ((300 - 2 * offset ) / (n - 1));
for (let i = 0; i < n; i++) {
scene.add(new Circle(i * spacing + offset, 50, 25))
}
scene.draw(ctx);
</script>
I loved it and i'm using it now... although i'm having trouble trying to positioning the balls inside my draw... see what I got so far and if you have a little more time to give me a hand in this <3 (I need to put the balls inside the third rect only no matter what width or height the user enter)
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
scene.clear();
context.beginPath();
context.strokeRect(zoomedX(0), zoomedY(0), zoomed(width), zoomed(height));
context.strokeRect(zoomedX(55), zoomedY(55), zoomed(width-10), zoomed(height-10));
context.strokeRect(zoomedX(60), zoomedY(60), zoomed(width-20), zoomed(height-20));
context.closePath();
let radius = 8;
let n = 3;
let lines = 3;
let offset = 68;
let offsetY = 68;
let spacing = ((width - 2 * offset ) / (n - 1));
let spacingY = ((height - 2 * offset ) / (lines - 1));
for (let i = 0; i < n; i++) {
for(let j = 0; j < lines ;j++){
scene.add(new Circle(i * spacing + offset, j * spacingY + offset, radius))
}
}
scene.draw(context);
}
I am basically trying to make several balls in line together and get the balls move up and down on the Y axis but each ball starts with a bit of delay so that in the end it should create infinite wave effects
I first tried with increasing velocity and although it creates the effect I want but it does it temporarily in that it keeps increasing the dynamics of the animation.
I think only if i can make the each ball starts animating at a different time it will solve this issue. any suggestion to this ?
check out the animation on my codepen https://codepen.io/jinnn0/pen/zgRrKm?editors=0010
const canvas = document.querySelector('canvas')
const c = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
function Circle(x, y, radius, color, velocity, amplitude) {
this.x = x
this.y = y
this.color = color
this.radius = radius
this.radian = 1
this.velocity = velocity
this.amplitude = amplitude
this.acceleration = 0.001
this.count = 0
this.draw = function(){
c.beginPath()
c.fillStyle = color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.lineWidth = 3
c.stroke()
c.fill()
c.closePath()
}
this.update = function(){
this.draw()
this.radian += this.velocity
this.y = y + Math.sin(this.radian) * this.amplitude
this.count ++
}
}
let circleArr;
function init(){
circleArr = []
let radius = 25
let x = radius
let y = 200
let color = "rgba(140, 140, 140)"
let velocity = 0.05
let amplitude = 100
for( let i =0; i < 10; i++ ) {
circleArr.push(new Circle(x, y, radius, color, velocity,
amplitude))
x += radius * 2
// velocity += 0.001
}
}
function animate(){
c.clearRect(0, 0, canvas.width, canvas.height)
for(let i = 0; i < circleArr.length; i++) {
circleArr[i].update()
}
requestAnimationFrame(animate)
}
init()
animate()
You can store the start timestamp using Date.now() then compare the ellapsed time since the start of the animation against a specified delay (in ms) for each ball (note that I've also changed the starting radian to 0 so that the balls don't "jump" to the bottom when the animation starts, and the start y so that we actually see the balls in the snippet preview):
const canvas = document.querySelector('canvas')
const c = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
const start = Date.now();
const delay = 2000;
function Circle(x, y, radius, color, velocity, amplitude, delay) {
this.x = x
this.y = y
this.color = color
this.radius = radius
this.radian = 0
this.velocity = velocity
this.amplitude = amplitude
this.acceleration = 0.001
this.count = 0
this.delay = delay
this.draw = function(){
c.beginPath()
c.fillStyle = color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.lineWidth = 3
c.stroke()
c.fill()
c.closePath()
}
this.update = function(ellapsed){
this.draw()
if (ellapsed > this.delay) {
this.radian += this.velocity
this.y = y + Math.sin(this.radian) * this.amplitude
this.count ++
}
}
}
let circleArr;
function init(){
circleArr = []
let radius = 25
let x = radius
let y = 130
let color = "rgba(140, 140, 140)"
let velocity = 0.05
let amplitude = 100
let delay = 0
for( let i =0; i < 10; i++ ) {
circleArr.push(new Circle(x, y, radius, color, velocity,
amplitude, delay))
x += radius * 2
delay += 100
// velocity += 0.001
}
}
function animate(){
// ellapsed time from the start (you should initialize start
// closer to the animation start if there is a noticeably long
// operation in between)
const ellapsed = Date.now() - start;
c.clearRect(0, 0, canvas.width, canvas.height)
for(let i = 0; i < circleArr.length; i++) {
// ellapsed is passed to the ball's update method
circleArr[i].update(ellapsed)
}
requestAnimationFrame(animate)
}
init()
animate()
<html>
<body>
<canvas />
</body>
</html>
Alternatively, you could initialize a start variable for each ball at the time we first call update on them (so that not all balls rely on a global start wich may not be meaningful for them)
edit:
After re reading your question I've noticed that you may look for another solution not involving delay. It's actually easier: just give each ball a different (incremented) starting radian.
Below I've replaced the additional delay parameter by a radian parameter:
function Circle(x, y, radius, color, velocity, amplitude, radian) {
this.x = x
this.y = y
this.color = color
this.radius = radius
this.velocity = velocity
this.amplitude = amplitude
this.acceleration = 0.001
this.count = 0
this.radian = radian
this.draw = function(){
c.beginPath()
c.fillStyle = color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.lineWidth = 3
c.stroke()
c.fill()
c.closePath()
}
this.update = function(){
this.draw()
this.radian += this.velocity
this.y = y + Math.sin(this.radian) * this.amplitude
this.count ++
}
}
Then instead of feeding ellapsed time to each ball I've initialized them with an incremented radian:
function init(){
circleArr = []
let radius = 25
let x = radius
let radian = 0
let y = 130
let color = "rgba(140, 140, 140)"
let velocity = 0.05
let amplitude = 100
let delay = 0
for( let i =0; i < 10; i++ ) {
circleArr.push(
new Circle(
x, y,
radius, color,
velocity, amplitude, radian
)
)
x += radius * 2
radian += Math.PI / 10
// velocity += 0.001
}
}
const canvas = document.querySelector('canvas')
const c = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
function Circle(x, y, radius, color, velocity, amplitude, startRadian) {
this.x = x
this.y = y
this.color = color
this.radius = radius
this.velocity = velocity
this.amplitude = amplitude
this.acceleration = 0.001
this.count = 0
this.radian = startRadian
this.draw = function(){
c.beginPath()
c.fillStyle = color
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
c.lineWidth = 3
c.stroke()
c.fill()
c.closePath()
}
this.update = function(){
this.draw()
this.radian += this.velocity
this.y = y + Math.sin(this.radian) * this.amplitude
this.count ++
}
}
let circleArr;
function init(){
circleArr = []
let radius = 25
let x = radius
let radian = 0
let y = 130
let color = "rgba(140, 140, 140)"
let velocity = 0.05
let amplitude = 100
let delay = 0
for( let i =0; i < 10; i++ ) {
circleArr.push(new Circle(x, y, radius, color, velocity,
amplitude, radian))
x += radius * 2
radian += Math.PI / 10
// velocity += 0.001
}
}
function animate(){
c.clearRect(0, 0, canvas.width, canvas.height)
for(let i = 0; i < circleArr.length; i++) {
circleArr[i].update()
}
requestAnimationFrame(animate)
}
init()
animate()
<html>
<body>
<canvas/>
</body>
</html>
It works even better but will change the starting position of the balls.
You can achieve pretty cool effect by combinating the two techniques (radian offset + delay).
I let you experiment with the different parameters.