Canvas Interaktion und Animation (2/3)

Viele Punkte (als Objekte) animieren

Diese Variante des letzten Punkt-Beispiels stellt jeden gemalten Punkt als Objekt dar, sodass jeder Punkt einzeln animiert werden kann.

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

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 = {
    x: x,
    height: canvas.height - y,
    speed: 0,
  };

  // 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 (animateDot(dot, deltaTime)) {
      drawDot(dot);
    } else {
      animatedDots.delete(dot);
    }
  }

  // advance animation time
  animationTime = now;

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

function animateDot(dot, deltaTime) {
  // update height from speed
  dot.height += deltaTime * dot.speed;

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

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

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

  // continue animation
  return true;
}

function drawDot(dot) {
  let dotRadius = 10;
  ctx.beginPath();
  ctx.arc(dot.x, canvas.height - dot.height - dotRadius, dotRadius, 0, 2.0*Math.PI);
  ctx.fill();
}

Auch wenn das Beispiel mit einfachen Objekten schon recht gut funktioniert, wäre es noch eleganter, wenn eine Punkt-Klasse verwendet wird.