6. Dynamisches Verhalten im Browser mit Server

6.4 Zeichnen mit Canvas

Gehen Sie auf index-01-draw-by-hand.html. Ändern Sie die Größe Ihres Fensters und sehen Sie, wie sich der Canvas (die schwarze Box) anpasst, und wie seine Maße angegeben werden.

Übung Adaptieren Sie den Code, sodass neben den Maßen des Canvas-Elements auch die Maße des ganzen Fensters angegeben wird.
Übung Öffnen Sie die Javascript-Konsole Ihres Browsers und geben Sie ein:
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(300, 400);
        context.stroke();
        context.lineTo(500, 400);
        context.stroke();
        context.lineTo(500, 700);
        context.stroke();
        context.lineTo(0, 0);
        context.stroke();
                        
Spielen Sie damit rum und zeichnen Sie ein paar Pfade. Dann tippen Sie
        context.strokeStyle = 'brown';
        context.stroke();
        context.fillStyle = 'aquamarine';
        context.stroke();
        context.fill();
Kreise und Kreisbögen können Sie zeichnen mit
        context.beginPath();
        context.strokeStyle = "red";
        context.arc(0.5 * cw, 0.5 * ch, 10, 0, 2 * Math.PI);
        context.stroke();
        context.fill();

Zeichnen Sie ein paar Sachen per Hand, um ein Gefühl dafür zu bekommen.

Ein Problem ist, dass die Graphik verschwindet, wenn die die Fenstergröße verändern. Das Problem können wir lösen, indem wir den Graphik erzeugenden Code in eine Funktion drawCanvas auslagern und die dann immer aufrufen, wenn sich die Fenstergröße ändern: window.onresize = drawCanvas;

Übung Legen Sie ein Datenformat für Graphikobjekte fest, zum Beispiel {type : "circle", x : 200, y : 300, radius : 20, fillColor = 'yellow', strokeColor = 'black'} und entsprechendes für Liniensegmente (wenn Sie wollen, natürlich gerne auch für ganze Pfade), und schreiben eine Funktion drawElement(element), die das dann zeichnet. Legen Sie in eine globalen Liste von solchen Objekten an. Ihre Funktion drawCanvas() soll dann die Liste durchgehen und alles zeichnen.

Relative Koordinaten

Eine weitere unschöne Eigenschaft ist, dass die Funktionen lineTo() etc. absolute Koordinaten verwenden, also in Pixeln, mit (0,0) als linke obere Ecke das Canvas-Objekts.

Übung Ändern Sie Ihr Datenformat und Ihre Funktion drawCanvas(), so dass sie relative Koordinaten verwendet, zum Beispiel mit (0,0) links oben oder (1,1) rechts unten.

Hinweis. Je nach Anwendung kann es mehr Sinn machen, dass (0,0) links unten steht und höhere y-Koordinaten dann weiter oben stehen.

Übung Recherchieren Sie im Internet: gibt es eine Möglichkeit, eine solche Koordinatentransformation dem Canvas ein für alle Mal mitzuteilen, oder müssen wir die jedes Mal von Hand machen?

Mausposition lesen

Übung Schauen Sie index-03-mouse-coordinates.html und versuchen Sie, zu verstehen, wie Mouse-Events verarbeitet werden. Fügen Sie der Webseite ein <span>-Element hinzu, das immer den Ort des letzten Maus-Klicks anzeigt.
Übung Kombinieren Sie Maus-Events mit der Übung, in der Sie Graphik-Objekte in einer Liste speichern. Zum Beispiel
  • Jeder Mausklick soll einen Kreis an dem angegebenen Ort hinzufügen, so dass Sie mit Mausklicks "zeichnen" können.
  • Wenn Sie ambitioniert sind: lassen Sie den Benutzer über Knöpfe oder Radio-Buttons zwischen zwei Zeichenmodi wechseln: Kreise zeichnen oder Liniensegmente zeichnen.
Übung Kombinieren Sie die vorherige Übung mit Web-Sockets. Wenn der Benutzer etwas zeichnet, soll die Seite das gezeichnete Objekt als JSON kodiert an den Web-Socket schicken. Also zum Beispiel den String {"type" : "circle", "x" : "0.2", "y" : "0.4"} und so weiter.

Wenn der Server eine Nachricht bekommt, soll er die einfach an alle Benutzer weiterleiten.

Wenn ein Benutzer (also die HTML-Seite) eine Websocket-Nachricht erhält, soll sie diese mit JSON.parse wieder dekodieren. Wenn es ein bekanntes Graphik-Objekt ist, also zum Beispiel element.type == "circle" oder element.type == "line", dann soll das Objekt der Liste von Graphik-Objekten hinzugefügt (und dann auch angezeigt) werden.

Übung Die Seite index-with-mouse.html ist völlig Browser-seitig. Es findet keine Kommunikation mit einem Webserver statt.

Erweitern Sie die Seite, dass mehrere Personen miteinander / gegeneinander spielen können. Der Server könnte zum Beispiel regelmäßig die neuen Positionen berechnen und an alle schicken. Die Spieler schicken ihre Richtungsänderungen an den Server, der das dann mit in Betracht zieht.

Hinweis. Seien Sie fürs erste nicht zu ehrgeizig. Sagen wir einfach, alle Clients, die sich verbinden, werden auf ein großes, gemeinsames Spielbrett geworfen.

Übung Denken Sie sich Erweiterungen aus. Zum Beispiel:
  • Wenn man sich anmeldet (mit Namen!), sieht man eine Liste aller Spieler, die noch offen für weitere Spieler sind.
  • Man kann create new game klicken.
  • Die Personn, die das Spiel erschaffen hat, kann es starten, wenn sie meint, dass genügend Personen drin sind.
  • Der Server weist einem Spieler die Rolle des Gejagten zu; dieser ist etwas schneller als der Rest; die anderen müssen ihn fangen.
Wenn Sie wollen, schicken Sie mir Ihren Code. Wir können ihn auch auf die Webseite stellen.

Tips

Nehmen Sie an, innerhalb einer Zeichenroutine (z.B. drawCanvas() rufen Sie eine Unterfunktion drawText(text) oder wie auch immer auf, die text einfügt, und zwar immer schwarz. Dann wird irgendwo in drawText(text) die Code-Zeile context.strokeStyle = 'black'; stehen. Wenn drawText() zurückkehrt und die umgebende Funktion drawCanvas() weitergeht, steht nun context.strokeStyle immer noch auf 'black', was zu Fehlern führen könnte. Ein Hack wäre
let old_color = context.strokeStyle;
drawText(text);
context.strokeStyle = old_color;
Aber vielleicht gibt es noch mehr Parameter, die Sie sich "merken" wollen. Besser, Sie verwenden context.save() und context.restore():
context.save();
drawText(text);
context.restore();
Damit werden die Kontext-Parameter, die vor dem Aufruf von drawText(text) galten, wiederhergestellt.