Ein Standardbeispiel für Anwendungen mit dem <canvas>-Element ist sicherlich die Analog-Uhr, da hier das Auflösen der eckigen und rechtwickligen Strukturen deutlich wird. Das Beispiel zeigt aber auch, wie Animationen mit dem neuen Element möglich werden.
Demonstriert wird das Beispiel an einer analogen Uhr im Bahnhofstil.
Da für den Quellcode lediglich Funktionen verwendet werden, die durch die Erweiterung ExplorerCanvas bereitgestellt und unterstützt werden, laufen die Scripts/Beispiele auch im IE.
Grundlage der vorgestellten Beispiele ist eine Grafik, die das Ziffernblatt darstellt. Vorstellbar wäre auch ein Konstruieren und Zeichnen mit den Funktionen und Methoden des <canvas>-Elements. Aus Performanzgründen wird jedoch darauf verzichtet.
Die Grafik wird einem image
-Objekt zugewiesen, wobei sie für moderne Browser
im PNG-Format, für sonstige Anzeigegeräte im GIF-Format vorliegt.
Zum Deklarieren des Image
-Objekts und dem korrekten Zuweisen der Quelle (src
) kann
folgendes Konstrukt verwendet werden.
var objImg = new Image(); // Falls es sich um einen IE vor Version 7 handelt if(/MSIE/.test(navigator.userAgent) && !window.opera && (Number(navigator.userAgent.split(";")[1].substr(navigator.userAgent.split(";")[1].search(/\d/))) < 7)){ objImg.src = "ziffernblatt.gif"; }else{ objImg.src = "ziffernblatt.png"; }
Als nächstes wird der Quellcode vorgestellt, mit dem die Uhr animiert wird. Als Grundlage dienen
hierfür neben den Methoden und Eigenschaften des <canvas>-Elements das
JavaScript-Objekt
Date
sowie zum periodischen Aufrufen der Anzeigefunktion die
Methode setTimeout
des Objekts
window.
function AnalogClock(objCanvas){ // Neues Datumsobjekt var objDate = new Date(); var intSek = objDate.getSeconds(); // Sekunden 0..59 var intMin = objDate.getMinutes(); // Minuten 0..59 var intHours = objDate.getHours()%12; // Stunden 0..11 // Kontext-Objekt var objContext = objCanvas.getContext("2d"); objContext.clearRect(0, 0, 150, 150); // Anzeigebereich leeren objContext.drawImage(objImg, 0, 0); // Ziffernblatt zeichnen objContext.save(); // Ausgangszustand speichern objContext.translate(75, 75); // Koordinatensystem in Mittelpkt des Ziffernblatts verschieben // Stunden objContext.save(); // Aktuelle Stunde zzgl. Minutenanteil über Drehung des Koordinatensystems // (kontinuierlicher Übergang zwischen zwei Stunden gewünscht, keine Sprung) objContext.rotate(intHours*Math.PI/6+intMin*Math.PI/360); objContext.beginPath(); // Neuen Pfad anlegen objContext.moveTo(0, 10); // Zeiger über Mitte hinaus zeichnen objContext.lineTo(0, -38); // Stundenzeiger im gedrehten Koord-Sys. um 38 Einheiten nach oben zeichnen // Linienstyle festlegen und zeichnen objContext.lineWidth = 4; objContext.strokeStyle = "#666"; objContext.stroke(); objContext.restore(); // Minuten objContext.save(); objContext.rotate(intMin*Math.PI/30); objContext.beginPath(); objContext.moveTo(0, 10); objContext.lineTo(0, -50); objContext.lineWidth = 4; objContext.strokeStyle = "#666"; objContext.stroke(); objContext.restore(); // Sekunden objContext.save(); objContext.rotate(intSek*Math.PI/30); objContext.beginPath(); objContext.moveTo(0, 10); objContext.lineTo(0, -50); objContext.lineWidth = 2; objContext.strokeStyle = "#a00"; objContext.stroke(); objContext.restore(); objContext.restore(); hTimer = window.setTimeout(function(){ AnalogClock(objCanvas);}, 1000); }
Zu Beginn wird ein Date-Objekt erstellt. Davon ausgehend werden die Sekunden, Minuten und Stunden ermittelt. Da die analoge Uhr nur 12 Stunden darstellen kann, wird vom 24-Stundenwert durch eine Modulo-Operation (%) lediglich Werte von 0 bis 11 errechnet.
Nachdem das Kontext-Objekt generiert wurde, werden mit der Methode clearRect zunächst die Inhalte des <canvas>-Elements gelöscht. Anschliessend wird das Ziffernblatt eingebunden (drawImage), der Canvas-Zustand gesichert (save) und der Ursprung in den Mittelpunkt des Ziffernblatts verschoben (translate). Das Sichern erfolgt, um am Ende der Routine wieder den "Ausgangszustand" herstellen zu können.
Jetzt erfolgt die Darstellung der Zeitkomponenten, wobei mit der Anzeige der Stunden begonnen wird. Da zum
Zeichnen des Stundenzeigers das Koordinatensystem gedreht wird, wird der Zustand erneut gespeichert.
Um die Stunden anzeigen zu können, wird der Vollkreis (2*PI) zunächst in 12 Teile zerlegt (PI/6). Die Multiplikation
mit der aktuellen Stunde gibt den Winkel α im Bogenmass, um zur gesuchten Stunden zu gelangen.
Da ein Springen des Zeigers beim Erreichen einer vollen Stunde nicht gewünscht ist, soll ein kontinuierliches
Vorrücken umgesetzt werden. Das wird realisiert, indem der Zeiger minutenweise um den entsprechenden Anteil
weitergedreht wird. Hierfür wird der Bereich, den der Stundenzeiger in einer Stunde überstreicht (PI/6) in 60
Teile zerlegt (PI/360). Die Multiplikation mit der aktuellen Minute ergibt den Winkel β, der zu
α addiert wird.
Jetzt kann das Koordinatensystem mit der Methode
rotate
um den gerade errechneten Winkel gedreht werden. Zum Abschluss der Stundendarstellung wird noch der Zeiger
in negative y-Richtung (= nach oben) eingezeichnet.
Die Vorgehensweisen bei den Darstellungen von Minuten und Sekunden erfolgen in identischer Weise.
Lediglich die Drehwinkel unterscheiden sich, da der Vollkreis in beiden Fällen in 60 Teile unterteilt
werden muss (PI/30).
Zudem werden die Zeiger in unterschiedlicher Länge, Farbe und Stärke dargestellt. Dies dient aber nur
der Unterscheidbarkeit, greift jedoch nicht in die Funktionalität ein.
In der ersten Variation werden die Sekunden abwechselnd als zunehmender bzw. abnehmender Kreisausschnitt dargestellt.
Nachstehend ohne weitere Beschreibung die Sequenz des Quellcodes, der für die Darstellung der Sekunden verantwortlich ist. Die entscheidenden Stellen wurden kommentiert.
objContext.save(); var intSek = objDate.getSeconds(); objContext.rotate(3*Math.PI/2); objContext.beginPath(); objContext.moveTo(0, 0); // Falls eine Minute voll ist if(intSek == 0) intCheck *= -1; // Falls ein zunehmender Kreisausschnitt dargestellt werden soll if(intCheck>0) // Kreisausschnitt von 0° bis aktuelle Sekunde objContext.arc(0, 0, 50, 0, intSek*Math.PI/30, false); // Falls ein abnehmender Kreisausschnitt dargestellt werden soll else // Kreisausschnitt von aktueller Sekunde bis 360° objContext.arc(0, 0, 50, intSek*Math.PI/30, 2*Math.PI, false); objContext.closePath(); objContext.fillStyle = "rgba(0, 33, 191, 0.5)"; objContext.fill(); objContext.restore();
In der zweiten Variante ist die Laufrichtung der Uhr umgekehrt. D.h. die Zeiger bewegen sich links herum. Um das zu realisieren, müssen nur die negativen Winkel den rotate-Methoden übergeben werden.