Canvas Interaktion und Animation (1/3)

Punkte malen

Dieses erste Beispiel zeigt, wie man mit dem Canvas über Mausklicks interagieren kann. Zur Berechnung der Position des Mausklicks im Canvas DOM-Element, wird die Position innerhalb des Viewports (= Anzeigebereich) für den Mausklicks über das MouseEvent und für das DOM-Element über getBoundingClientRect herangezogen.

Canvas Mouse Position

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

canvas.addEventListener('click', onClick);

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

  // draw black dot
  ctx.beginPath();
  ctx.arc(x, y, 10, 0, 2 * Math.PI);
  ctx.fill();
}

Punkte animieren

Dieses Beispiel führt eine Animation der gemalten Punkte ein. Dazu wird die Window-Funktion requestAnimationFrame verwendet, die den Aufruf einer gegebenen Funktion vor der nächsten Bildschirmaktualisierung anfordert.

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

// physics animation state
let dotX = window.innerWidth / 2;
let dotHeight = 0; // pixel above bottom
let dotSpeed = 0; // pixel per second
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;

  // initialise animation
  dotX = x;
  dotHeight = canvas.height - y;
  dotSpeed = 0;

  // start animation
  animationTime = 0.001 * performance.now();
  requestAnimationFrame(onAnimationFrame);
}

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

  if (animateDot(deltaTime)) {
    // clear canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // draw dot
    let dotRadius = 10;
    ctx.beginPath();
    ctx.arc(dotX, canvas.height - dotHeight, dotRadius, 0, 2*Math.PI);
    ctx.fill();

    // advance animation time
    animationTime = now;

    // continue animation
    requestAnimationFrame(onAnimationFrame);
  }
}

function animateDot(deltaTime) {
  // update height from speed
  dotHeight += deltaTime * dotSpeed;

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

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

  // update speed from acceleration
  dotSpeed += deltaTime * dotAcceleration;

  // continue animation
  return true;
}

Dieses Beispiel zeigt eindrücklich die Grenzen eines Programms, das den Zustand eines interaktiven und/oder animierten Objekts - hier ein Punkt - mit einer Reihe von einfachen Variablen implementiert, mit denen lediglich der Zustand eines einzigen Objekts dargestellt werden kann.

Um den Zustand mehrerer Objekte - mehrerer Punkte - abzubilden, bietet sich die Verwendung von JavaScript-Objekten an.