Canvas Interaktion und Animation (3/3)

Viele Punkte (einer Punkt-Klasse) animieren

Diese Variante des Punkt-Beispiels implementiert eine Punkt-Klasse (Dot), die den Zustand der Animation sowie zwei Methoden - zum Berechnen der Animation und zum Zeichnen des Punkts - enthält.

HTML<canvas id="myCanvas" width="400" height="400"></canvas>
CSS#myCanvas {
  border: 1px solid;
}
JavaScriptconst canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// physics animation parameters
const dotAcceleration = -2000; // pixel per second^2
const bouncingLossFactor = 0.9; // speed loss factor when rebouncing at the bottom
const minBouncingSpeed = 25; // minimum speed stopping

const animatedDots = new Set(); // list of animated dots
let animationTime = null; // current animation time

class Dot {
  constructor(x, height, size) {
    this.x = x;
    this.height = height;
    this.speed = 0;
    this.size = size;
  }

  animate(deltaTime) {
    // update height from speed
    this.height += deltaTime * this.speed;

    // bounce at bottom
    if (this.height <= 0) {
      this.height = -this.height; // correct of negative y-positions
      this.speed *= -bouncingLossFactor; // invert speed and apply loss

      // stop animation when rebouncing too slowly
      if (this.speed < minBouncingSpeed) {
        return false;
      }
    }

    // update speed from acceleration
    this.speed += deltaTime * dotAcceleration;

    // continue animation
    return true;
  }

  draw(ctx) {
    ctx.beginPath();
    ctx.arc(this.x, canvas.height - this.height - this.size, this.size, 0, 360);
    ctx.fill();
  }
}

canvas.addEventListener('click', onClick);

function onClick(evt) {
  const canvasClientRect = canvas.getBoundingClientRect();
  const x = evt.clientX - canvasClientRect.x;
  const y = evt.clientY - canvasClientRect.y;

  // create dot
  const dot = new Dot(x, canvas.height - y, 10);

  // add dot to set of animated dots
  animatedDots.add(dot);

  // start animation on first dot
  if (animatedDots.size === 1) {
    animationTime = 0.001 * performance.now();
    requestAnimationFrame(onAnimationFrame);
  }
}

function onAnimationFrame() {
  const now = 0.001 * performance.now();
  const deltaTime = now - animationTime;

  // clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  for (let dot of animatedDots) {
    if (dot.animate(deltaTime)) {
      dot.draw(ctx);
    } else {
      animatedDots.delete(dot);
    }
  }

  // continue animation
  if (animatedDots.size > 0) {
    animationTime = now;
    requestAnimationFrame(onAnimationFrame);
  }
}