Home > Net >  Animating multiple circles in a canvas
Animating multiple circles in a canvas

Time:09-17

I'm trying to make an animation inside a canvas: here, a numbered circle must be drawn and move from left to right one single time, disappearing as soon as it reaches the end of animation.

For now I managed to animate it in loop, but I need to animate at the same time (or with a set delay) multiple numbered circles, strating in different rows (changing y position) so they wont overlap.

Any idea how can I manage this? my JS code is the following:

// Single Animated Circle - Get Canvas element by Id
var canvas = document.getElementById("canvas");

// Set Canvas dimensions
canvas.width = 300;
canvas.height = 900;

// Get drawing context
var ctx = canvas.getContext("2d");

// Radius
var radius = 13;
// Starting Position
var x = radius;
var y = radius;

// Speed in x and y direction
var dx = 1;
var dy = 0;

// Generate random number
var randomNumber = Math.floor(Math.random() * 60)   1;

if (randomNumber > 0 && randomNumber <= 10) {
  ctx.strokeStyle = "#0b0bf1";
} else if (randomNumber > 10 && randomNumber <= 20) {
  ctx.strokeStyle = "#f10b0b";
} else if (randomNumber > 20 && randomNumber <= 30) {
  ctx.strokeStyle = "#0bf163";
} else if (randomNumber > 30 && randomNumber <= 40) {
  ctx.strokeStyle = "#f1da0b";
} else if (randomNumber > 40 && randomNumber <= 50) {
  ctx.strokeStyle = "#950bf1";
} else if (randomNumber > 50 && randomNumber <= 60) {
  ctx.strokeStyle = "#0bf1e5";
}

function animate3() {
  requestAnimationFrame(animate3);

  ctx.clearRect(0, 0, 300, 900);

  if (x   radius > 300 || x - radius < 0) {
    x = radius;
  }

  x  = dx;

  ctx.beginPath();
  ctx.arc(x, y, 12, 0, Math.PI * 2, false);
  ctx.stroke();
  ctx.fillText(randomNumber, x - 5, y   3);
}

// Animate the Circle

animate3();
<canvas id="canvas"></canvas>

CodePudding user response:

Here is a solution which doesn't use classes as such and separates the animation logic from the updating - which can be useful if you want more precise control over timing.

// Some helper functions
const clamp = (number, min, max) => Math.min(Math.max(number, min), max);

// Choose and remove random member of arr with equal probability 
const takeRandom = arr => arr.splice(parseInt(Math.random() * arr.length), 1)[0]

// Call a function at an interval, passing the amount of time that has passed since the last call
function update(callBack, interval) {
  let now = performance.now();
  let last;
  setInterval(function() {
    last = now;
    now = performance.now();
    callBack((now - last) / 1000);
  })
}

const settings = {
  width: 300,
  height: 150,
  radius: 13,
  gap: 5,
  circles: 5,
  maxSpeed: 100,
  colors: ["#0b0bf1", "#f10b0b", "#0bf163", "#f1da0b", "#950bf1", "#0bf1e5"]
};
const canvas = document.getElementById("canvas");
canvas.width = settings.width;
canvas.height = settings.height;
const ctx = canvas.getContext("2d");

// Set circle properties
const circles = [...Array(settings.circles).keys()].map(i => ({
  number: i   1,
  x: settings.radius,
  y: settings.radius   (settings.radius * 2   settings.gap) * i,
  radius: settings.radius,
  dx: settings.maxSpeed * Math.random(), // This is the speed in pixels per second
  dy: 0,
  color: takeRandom(settings.colors)
}));

function drawCircle(circle) {
  ctx.strokeStyle = circle.color;
  ctx.beginPath();
  ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, false);
  ctx.stroke();
  ctx.fillText(circle.number, circle.x - 5, circle.y   3);
}

function updateCircle(circle, dt) {
  // Update a circle's position after dt seconds
  circle.x = clamp(circle.x   circle.dx * dt, circle.radius   1, settings.width - circle.radius - 1);
  circle.y = clamp(circle.y   circle.dy * dt, circle.radius   1, settings.height - circle.radius - 1);
}

function animate() {
  ctx.clearRect(0, 0, settings.width, settings.height);
  circles.forEach(drawCircle);
  requestAnimationFrame(animate);
}

update(dt => circles.forEach(circle => updateCircle(circle, dt)), 50);
animate();
<canvas id="canvas" style="border: solid 1px black"></canvas>

CodePudding user response:

Here I transformed your sample code into a class ...
We pass all the data as a parameter, you can see that in the constructor, I simplified a lot of your code to keep it really short, but all the same drawing you did is there in the draw function

Then all we need to do is create instances of this class and call the draw function inside that animate3 loop you already have.

You had a hardcoded value on the radius:
ctx.arc(x, y, 12, 0, Math.PI * 2, false)
I assume that was a mistake and fix it on my code

var canvas = document.getElementById("canvas");
canvas.width = canvas.height = 300;
var ctx = canvas.getContext("2d");

class Circle {
  constructor(data) {
    this.data = data
  }

  draw() {
    if (this.data.x   this.data.radius > 300 || this.data.x - this.data.radius < 0) {
      this.data.x = this.data.radius;
    }
    this.data.x  = this.data.dx;

    ctx.beginPath();
    ctx.arc(this.data.x, this.data.y, this.data.radius, 0, Math.PI * 2, false);
    ctx.stroke();
    ctx.fillText(this.data.number, this.data.x - 5, this.data.y   3);
  }
}

circles = []
circles.push(new Circle({radius:13, x: 10, y: 15, dx: 1, dy: 0, number: "1"}))
circles.push(new Circle({radius:10, x: 10, y: 50, dx: 2, dy: 0, number: "2"}))


function animate3() {
  requestAnimationFrame(animate3);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  circles.forEach(item => item.draw());
}
animate3();
<canvas id="canvas"></canvas>

Code should be easy to follow let me know if you have any questions

  • Related