Ich habe bei den Musik-Animationen die ersten Erfahrungen mit dem Canvas-Element gesammelt, und ich muss sagen, ich bin sehr angetan. Das eröffnet wirklich ungeahnte Möglichkeiten, und ich finde es wesentlich eingänginger und intuitiver zu bedienen als CSS-Animationen, wo man sich ja ewig mit der Cross-Browser-Kompatibilität herumschlagen muss. Canvas wird von den meisten gängigen modernen Browsern unterstützt, genauere Info hier auf der Seite der Canvas Community.
Statt langer Vorreden legen wir gleich los: passend zum Jahreswechsel habe ich eine kleine Feuerwerksanimation geschrieben. Sowas gibt es schon, auch als downloadbare Freeware, aber mir kam der Sourcecode der Beispiele immer unnötig aufgeblasen vor, ich dachte mir, das muss doch auch einfacher gehen…. und es geht einfacher, deutlich.
Die Vorgabe
Meine Vorgabe ist folgende: auf einem dunklen Sternenhimmel sollen farbige Feuerwerksraketen sternförmig explodieren, an zufälligen Stellen auf dem Canvas. Die Animation soll per Klick startbar sein und dann in einer Endlosschleife laufen. Aussehen soll das ganze etwa so:
Dafür brauche ich zwei Zeitschleifen. Die äussere Schleife wird endlos wiederholt (setInterval…) und erzeugt immer einen neuen zufälligen Mittelpunkt der nächsten Explosion. An einem gegebenen Mittelpunkt startet dann die zweite Zeitschleife, hier soll die Sternenexplosion in mehreren Iterationsschritten von der Mitte aus zeitverzögert ablaufem, dafür nehme ich einen setTimeout. Für die Skriptsteuerung brauche ich Jquery, das binde ich mal lokal ein.
Los gehts: wir basteln uns erst einmal die Leinwand, den canvas, das ist straightes HTML:
<canvas id="leinwand" width="400" height="400" style="display: block; background: url('sternenhimmel.jpg')" ></canvas>
Ich lege hier gleich mal einen netten Sternenhimmel in den Hintergrund.
Dann brauchen wir noch einen Startknopf:
<input type="button" value = "Abspielen" onclick = "kontrolle()" >
Die Funktion kontrolle() tut nichts weiter als die Funktion zeichnen() aufzurufen, hier könnte man noch Parameter übergeben, z.B. eine Wiederholungsrate oder die Dauer einer Schleife, aber ich hab das mal gelassen und setze die entsprechenden Werte direkt im Code. Als allererste Massnahme definiert die Funtkion zeichnen() einen Canvas-2d-Context, mit dem arbeiten wir dann weiter:
function zeichnen(param) { // get the context from the canvas to draw on var lwd = $("#leinwand").get()[0].getContext("2d"); ...
lwd ist jetzt unser Zeichenbrett.
Wie man einen Stern zeichnet
Wir zäumen das Pferd jetzt mal von hinten auf. Wie zeichnet man überhaupt einen Stern auf den Canvas? Man braucht einen festen Mittelpunkt in Form einer x- und einer y-Koordinate, und einen festen Radius. Dann braucht man noch eine mathematische Formel, die einem ausrechnet bis zu welchem Endpunkt ein Strahl zu einem bestimmten Winkel gesetzt werden soll. Die Formel packt man in eine for…Schleife, und zählt den Winkel einfach hoch, bis man einmal rum ist.
//Begin funktion strahlen zeichnen function strahlen(fx,fy,radius){ var rad = radius; //Beginn strahlen graduell for (grad = -1; grad <=1; grad = grad + 0.2){ theta = grad*Math.PI; lwd.moveTo(fx, fy); lwd.lineTo(fx + rad * Math.cos(theta), fy + rad * Math.sin(theta)); //jeder Strahl eine andere Farbe lwd.strokeStyle = "hsl("+Math.floor((Math.random()*359))+", 100%, 50%)"; lwd.stroke(); //Nach der letzten Schleife Leinwand leer machen if (radius == 100){lwd.clearRect(0, 0, 400, 400);} //**Pfad neu zeichnen (ohne "Geisterbilder") lwd.beginPath(); }//end strahlen graduell } // Ende von fucntion strahlen zeichnen
Man setzt den Cursor mit .moveTo auf den Mittelpunkt, und zeichnet mit dem .lineTo den Strahl bis zur berechneten Koordinate. .stroke() führt die Zeichnung aus. Dann wird der Winkel hochgesetzt, man zeichnet den nächsten Strahl, bis man einmal um den Kreis von -1 bis +1 herum ist. Ich hab hier auch noch die Farbdefinition mit hineingepackt und bestimme für jeden Strahl eine zufällige hsl-Farbe, das macht sich ganz hübsch.
Nach dem letzten Aufruf (gegebener maximaler Radius von 100 ist erreicht) macht man die Leinwand leer, damit der Stern auch wieder verschwindet. .beginPath() macht dann noch klar Schiff auf dem internen Pfad-Objekt, wenn das fehlt hat man dann später „Geisterbilder“ von den vorher bereits gezeichneten Sternen.
Die erste Animation: Sterne mit steigenden Radien
Damit der Stern nicht in einem Flash auftaucht, sondern sich wie bei einer Explosion von innen heraus vergrößert, rufe ich die soeben definierte Funktion strahlen() mehrfach auf, immer mit dem selben Mittelpunkt, aber mit stetig wachsenden Radien. Dafür benutze ich einen setTimeout:
//*********begin function sternchen mit timeout function sternchen(koord1,koord2){ var i = 10; // zähler für den Radius auf 10 (px) //Zufälliger Mittelpunkt des sterns kommt als Parameter rein koord zwischen 1 und 400 var x1 = koord1; var y1 = koord2; function myLoop () { // Loop function definieren setTimeout(function () { // setTimeout Wenn die Loop-Funktion aufgerufen wird strahlen(x1,y1,i); // Strahlen zeichnen an festem Mittelpunkt und Radius i=i+10;; // Radius um 10 (px) hochsetzen if (i <=100) { // Radius <= 100? myLoop(); // Loop function erneut aufrufen, Strahlen mit neuem Radius zeichnen } }, 50) // .. setTimeout() auf 50 ms } //*******Ende von function myloop myLoop(); //Startet den Loop } //******ende von function sternchen
Mein i geht in 10er-Schritten hoch, das benutze ich als Radius für den explodierenden Stern in px. Die x- und y-Koordinate des Mittelpunkts bleiben immer gleich, die hab ich als Parameter reingegeben. Der Timeout läuft auf 50 Millisekunden, das ergibt eine hübsch gleichmässige Bewegung. Die Schleife bricht erst ab, wenn der maximale Radius von 100 px erreicht ist.
Die zweite Animation: alle halbe Sekunde ein neuer zufälliger Mittelpunkt
Meine Funktion sternchen() wird jetzt alle 500 my aufgerufen, und bei jedem Aufruf wird eine zufällige x- und y-Koordinate für den Mittelpunkt der Explosion generiert. Das ganze wird mit einem setInterval in einer Endlosschleife gesteuert:
//Beginn Feuerwerk Endlosschleife
//passiert alle x millisekunden
var drawLinesInterval = setInterval(function() {
//Leinwand putzen
lwd.clearRect(0, 0, 400, 400);
//Zufälligen Mittelpunkt des Sterns bestimmen Koordinaten zwischen 1 und 400
var koord1 = Math.floor(Math.random() * 20 + 1)*20;
var koord2 = Math.floor(Math.random() * 20 + 1)*20;
//Funktion zum Sternzeichnen aufrufen
sternchen(koord1,koord2);
}, 500); // end drawLinesInterval
Das wars auch schon! Morgen packe ich noch eine Demo mit dazu. da könnt ihr euch dann den Source direkt anschauen. Canvas macht Laune!