In meinem großen HTML5-Artikel kam letztens ein neues Element etwas zu kurz, das eigentlich etwas besseres verdient hatte. Denn dieses Element, <canvas>, hat viel Potenzial. Von dezenten optischen Extras bis hin zu kompletten eigenen Programmoberflächen kann man damit einiges anstellen und alles was man dafür braucht, ist ein klein wenig Ahnung von HTML und Javascript.

Was ist <canvas> und wozu ist es gut?

Das englische Wort Canvas bedeutet auf deutsch Leinwand und ist ziemlich genau das: Eine Leinwand, auf die man mit Javascript dynamisch Bitmap-Grafiken malen kann. Erfunden wurde das Element vom Webkit-Team und später sind auch Mozilla und Opera auf den <canvas>-Zug aufgesprungen. Damit ist das das Element heutzutage in alle anständigen Browser implementiert (für den IE gibt es einen Trick – dazu später mehr) und Teil des werdenden HTML5-Standards.

Die Möglichkeiten von <canvas> sind relativ weit gefasst. Wann immer man man dynamisch Grafiken erzeugen möchte, kann man das mit <canvas> machen. Beispiele sind z.B. die bekannte Image-Replace-Technik Typeface.js, die experimentelle Online-IDE Bespin (quasi ein komplettes mit Canvas geschriebenes Programm) und das Javascript-Toolkit MochaUI. Das sind alles relativ mächtige Beispiele, aber man kann auch getrost kleinere Brötchen backen – sofern man nur ein wenig Ahnung von HTML und Javascript hat, kann man mit Canvas schon etwas anstellen. Und genau darum geht es im folgenden.

Wie bindet man das <canvas>-Element ein?

Sowohl ein öffnender als auch ein schließender Tag werden für <canvas> gebraucht – zwischen den Tags kann man Fallback-Content notieren, also Inhalt, der ausgegeben wird wenn das <canvas>-Element selbst nicht angezeigt werden kann. Breite und Höhe sollten als Attribute angegeben werden – wenn man die Maße per CSS festlegt, wird der <canvas>-Inhalt ausgehend von der Ausgangsgröße entsprechend verzerrt.

<canvas width="100" height="100">
    Fallback-Inhalt
</canvas>

Eine ID zu vergeben ist meist auch sinnvoll – man will ja das Element irgendwann mit Javascript beackern können.

Wie arbeitet man mit <canvas>?

Ein <canvas>-Element stellt einen oder mehrere sogenannte rendering contexts zur Verfügung, quasi Ebenen auf denen man zeichnen kann. Der im Moment einzige Context ist der 2D-Context – an einem 3D-Context wird aber gearbeitet. Für den Moment vergessen wir also einfach all das und beschränken uns auf den 2D-Context. Um mit <canvas> erste Schritte zu machen, brauchen wir nur zwei Dinge: ein <canvas>-Element in einem HTML-Dokument und eine Javascript-Funktion, die auf dem <canvas>-Element herummalt. Das Canvas-Element könnte so aussehen:

<canvas id="testcanvas1" width="300" height="200">
    Dein Browser kann diese Grafik nicht darstellen.
</canvas>

Es enthält Maße, Fallback-Content und eine ID zur Steuerung. Wenn wir jetzt dieses Rechteck mit einer Farbe füllen wollten, bräuchten wir nur 8 Zeilen Javascript:

function drawCanvas(){
    var canvas = document.getElementById('testcanvas1');
    if(canvas.getContext){
        var context = canvas.getContext('2d');
        context.fillStyle = "rgb(255, 0, 255)";
        context.fillRect(0, 0, canvas.width, canvas.height);
    }
}

Zur Erläuterung:

  • var canvas = document... holt das <canvas>-Element anhand seiner ID
  • var context = canvas.getContext('2d') holt den 2D-Context in die handliche context-Variable. Das brauchen wir, weil wir letzlich nicht auf dem <canvas>-Element selbst, sondern auf einem Context des Elements zeichnen.
  • context.fillStyle = "rgb(255, 0, 255)" setzt die Füllfarbe für alles, was wir im Context hiernach zeichnen werden auf ein schönes, kräftiges Pink
  • context.fillRect(0, 0, canvas.width, canvas.height) zeichnet ein gefülltes Rechteck, beginnend bei den Koordinaten 0,0 und endend bei der unteren rechten Ecke des <canvas>-Elements, definiert durch die gesamte Höhe und Breite

Jetzt braucht man nur noch die Funktion drawCanvas() irgendwie auszuführen (z.B. mittels <body onload="drawCanvas()">) und schon hat man ein fertiges, funktionierendes Canvas-Beispiel.

Ein einfaches Canvas-Beispiel

Was kann man genau alles mit <canvas> anstellen?

Natürlich kann man mit <canvas> einiges mehr anstellen als nur pinke Flächen malen. Die komplette API ist in den offiziellen HTML5-Spezifikationen sehr erschöpfend behandelt. Für eine etwas bekömmlichere Darstellung, die auf zwei A4-Seiten passt und obendrein noch bebildert ist, sei dieser Cheat Sheet aufs allerwärmste empfohlen.

Kurz gesagt kann man Pfade zeichnen, vorhandene Bilder manipulieren, Text scheiben und mehr. Die eingangs erwähnten Beispiele wie Bespin und MochaUI machen deutlich, dass man mit Canvas sehr viel anstellen kann. Man muss es eben nur wollen. Drei weitere (einfache) Beispiele gefällig?

Canvas-Beispiel 1: Ein Bild drehen

Man kann in ein <canvas>-Element Bilder oder auch den Inhalt anderer <canvas>-Elemente zeichnen. Das könnte zum Beispiel so aussehen. Im Prinzip ist das das gleiche Spiel wie beim Einführungs-Beispiel:

function drawCanvas(){
var canvas = document.getElementById('testcanvas1');
var img = new Image();
img.onload = function(){
        if(canvas.getContext){
            var context = canvas.getContext('2d');
            context.translate(200, 0);
            context.rotate(90 * Math.PI/180);
            context.drawImage(img, 0, 0, 200, 200);
        }
    }
    img.src = 'test.png';
}
  • var img = new Image() erstellt ein neues img-Element, das als Referenz für unser canvas-Element dient.
  • context.translate(200, 0) positioniert den Punkt, um den der Context rotieren wird
  • context.rotate(90 * Math.PI/180) macht eine 90°-Drehung
  • context.drawImage(img, 0, 0) zeichnet das Bild img ausgehend von den Koordinaten 0,0.

Weil in diesem Beispiel der gesamte Context und nicht das Bild rotiert wird, kann man die Funktion drawCanvas() immer wieder aufrufen um das Ganze in 90°-Schritten immer weiter zu drehen.

Ein Bild mit Canvas drehen

Beispiel 2: Eine kleine Animation

Animieren ist einfach: man zeichnet einfach Frame für Frame das <canvas>-Element neu. So kann man zum Beispiel ein paar halbtransparente Quadrate durch die Gegend segeln lassen. Man nehme einfach wieder eine drawCanvas()-Funktion …

function drawCanvas(){
    var canvas = document.getElementById('testcanvas1');
    if(canvas.getContext){
        var context = canvas.getContext('2d');
        animate(context, 31, 69, 1, -1, 8, 25, 1, 1);
    }
}

…  und eine mit einem Timer versehene animate()-Funktion, die nichts weiter macht, als anhand eines Haufens von Koordinaten Frame für Frame alle neu zu zeichnen.

function animate(context, ax, ay, adx, ady, bx, by, bdx, bdy){
    setTimeout(function(){
        //Bewegen und Abprallen für Quadrat A
        if(ax == 150){
            adx = -1;
        }
        else{
            if(ax == 0){
                adx = 1;
            }
        }
        if(ay == 150){
            ady = -1;
        }
        else{
            if(ay == 0){
                ady = 1;
            }
        }
        ax = ax+adx;
        ay = ay+ady;
        //Bewegen und Abprallen für Quadrat B
        if(bx == 150){
            bdx = -1;
        }
        else{
            if(bx == 0){
                bdx = 1;
            }
        }
        if(by == 150){
            bdy = -1;
        }
        else{
            if(by == 0){
                bdy = 1;
            }
        }
        bx = bx+bdx;
        by = by+bdy;
        // Alles neu zeichnen
        context.clearRect(0, 0, 200, 200);
        context.fillStyle = "rgba(0, 0, 255, 0.5)";
        context.fillRect(ax, ay, 50, 50);
        context.fillStyle = "rgba(0, 255, 0, 0.5)";
        context.fillRect(bx, by, 50, 50);
        self.animate(context, ax, ay, adx, ady, bx, by, bdx, bdy);
    }, 0);
}

Einfach bei jedem Frame mit context.clearRect(0, 0, 200, 200) die Canvas putzen und die Quadrate an ihren neuen Koordinaten zeichnen.

Eine einfache Animation mit Canvas

Beispiel 3: Paint

Richtig Interessant wird <canvas> erst, wenn man den Benutzer richtig mitmachen lässt. So ist zum Beispiel ein (sehr sehr rudimentäres) Malprogramm recht einfach machbar. Man nehme bei Mausbewegung die Koordinaten …

var x, y;
canvas.onmousemove = function(e){
    x = e.clientX-canvas.offsetLeft;
    y = e.clientY-canvas.offsetTop;
    kx.innerHTML = x;
    ky.innerHTML = y;
    paint();
}

…  und male, falls die Maustaste gedrückt ist, einfach an der Mausposition eine Fläche:

var active = false;
canvas.onmousedown = function(){ active = true; }
canvas.onmouseup = function(){ active = false; }
function paint(){
    if(active){
        context.fillRect(x, y, 10, 10);
    }
}
Ein einfaches Malprogramm mit Canvas

Ist wie gesagt sehr rudimentär, aber es ein Beispiel dafür, wie man <canvas> interaktiv ensetzen kann.

Und was machen wir mit dem Internet Explorer?

Dass keine einzige Version des Internet Explorer mit <canvas> etwas anfangen kann, ist nicht überraschend, wenn auch ärgerlich. Glücklicherweise gibt es ExplorerCanvas. Das ist ein Script, dass versucht, <canvas> mit diversen proprietären IE-Technologien wie z.B. VML nachzubilden.

Die Verwendung von ExplorerCanvas ist denkbar simpel: Einfach das Script einbinden und weitermachen wie bisher.

<!--[if IE]>
    <script type="text/javascript" src="excanvas.js"></script>
<![endif]-->

Einige <canvas>-Features wie radialGradient funktionieren nicht oder leicht anders als im Original, aber das ist eben der Internet Explorer. Was will man machen?

Links zum Thema