Zeichnen mit Canvas

Sichern und wiederherstellen von Canvas-Zuständen

Die Möglichkeiten mit Canvas-Elementen zu zeichnen beinhalten auch die Grafik zu animieren. Jedoch ist die Animation zugleich auch nur über ein paar Umwege korrekt zu erreichen.
Die wohl größte Limitation bei der Animation ist es, dass sobald die "Shapes" gezeichnet sind, bleiben sie so. Auch für die nächste Veränderung. Wenn wir also eine Shape bewegen wollen, müssen wir sie entfernen und erneut zeichnen und alles andere auch! Das kostet bei komplexen Bildern einiges an Rechenzeit.
Das erneute Zeichnen können erleichtern, indem wir den Zustand des Canvas zwischenspeichern und später erneut laden. Dazu nutzen wir folgenden Methoden:

ctx.save()
ctx.restore()

save() speichert, wie man vermuten kann, den aktuellen Zustand der Zeichnung. Zu einem Zustand gehören sämtliche Einstellungen, wie z.B. fillStyle, globalAlpha usw.. restore() hingegen stellt den gespeicherten Zustand wieder her. Save und Restore arbeiten wie auf einem Stack. Das bedeutet, dass save() mehrmals ausgeführt werden kann und restore dann den auf dem Stack obersten gespeicherten Zustand läd.

Prinzipien der Animation

Wollen wir nun unser Canvas mit Animationen belegen, gilt es folgende Schritte in der vorgebenen Reihenfolge auszuführen.

  1. Clear the canvas
    Wir müssen alle Elemente, die wir vorher gezeichnet haben, entfernen. Dazu eignet sich die Methode clearRect().
  2. Save the canvas state
    Sobald wir irgendwelche Einstellungen ändern, die den Zustand verändern, sollten wir den Ausgangszustand gespeichert haben. Denn wir wollen bei jeder Animation den Ursprungszustand als Ausgangsgrafik haben und jede Veränderung von da aus animieren.
  3. Draw animated shapes
    Hier führen wir die eigentliche Animation bzw. das Rendering aus.
  4. Restore the canvas state
    Wir stellen den gespeicherten Zustand wieder her, bevor wir ein neues Frame redern.

Kontrolle über die Animation

Das Timing, wann welches Frame gezeichnet wird, spielt bei Animationen eine besonders wichtige Rolle. In der Regel sehen wir die Zeichnung eines Canvas dann, wenn das Skript, welches sie zeichnet, zuende gelaufen ist. Das zum Beispiel macht es unmöglich eine Animation mit einer for-Schleife zu animieren. Doch es gibt andere Wege den Zeitpunkt des Renderings zu kontrollieren.

setInterval(function, delay)
setTimeout(function, delay)
requestAnimationFrame(callback)

Die genannten drei Funktionen, die vom window-Objekt angeboten werden, erledigen diesen Job ausreichend gut. setInterval() führt die übergebene Callback-Funktion im Abstand des übergebenen Delays (in Millisekunden) aus. setTimeout() führt den übergebenen Callback nach dem spezifizierten Delay aus. Und requestAnimationFrame() fordert den Browser auf für den übergebenen Callback vor dem nächsten Repaint eine Animation auszuführen.

Nun folgt eine Beispielanimation, welche aus dem Mozilla Developer Network entnommen ist. Sie demonstriert, wie sich mit verhältnismäßig wenig Code (<40 Zeilen) eine doch sehr ansehnliche Animation erstellen lässt.

Die resultierende Grafik ist ein kleines animiertes Sonnenystem

Der nachfolgende Code beschreibt die Animation des Sonnensystems:

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
window.requestAnimationFrame(draw);
}

function draw() {
var ctx = document.getElementById('canvas').getContext('2d');

ctx.globalCompositeOperation = 'destination-over';
ctx.clearRect(0,0,300,300); // clear canvas

ctx.fillStyle = 'rgba(0,0,0,0.4)';
ctx.strokeStyle = 'rgba(0,153,255,0.4)';
ctx.save();
ctx.translate(150,150);

// Earth
var time = new Date();
ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
ctx.translate(105,0);
ctx.fillRect(0,-12,50,24); // Shadow
ctx.drawImage(earth,-12,-12);

// Moon
ctx.save();
ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
ctx.translate(0,28.5);
ctx.drawImage(moon,-3.5,-3.5);
ctx.restore();

ctx.restore();

ctx.beginPath();
ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit
ctx.stroke();

ctx.drawImage(sun,0,0,300,300);

window.requestAnimationFrame(draw);
}

init();

Bei weiterem Intresse an Canvas-Elementen kann ich die ausführlichen Ressourcen des Mozilla Developer Network empfehlen. Diese dienten mir aus als Quelle für den Großteil den Inhalts dieser Seite.

Zurück zu Advanced