Vorwort

Das in HTML5 eingeführte Canvas- Element ermöglicht die dynamische Generierung von Bitmap-Bildern mithilfe von JavaScript-Code.

Die Grundeinstellung ist etwas kompliziert, die beteiligten Objekte selbst sind schlecht gestaltet, was auch auf eine echte Abnahme der Entwicklung der Standards zurückzuführen ist . Es gibt aber auch eine Kombination von Gründen, die das Canvas-Element zum Lernen und Trainieren wert machen: Es ist das zukünftige Element für Grafiken und sogar Videos in HTML, es ist äußerst leistungsfähig und in Kombination mit seinem natürlichen Lebensraum einer vollständigen Skriptsprache flexibel und voll anpassbar an alle Bedürfnisse.

Die Schnittstellen der JavaScript-Objekte sind unübersichtlich, Aktionen werden nicht ordnungsgemäß getrennt und die Wirkung der Methoden hängt von ungewöhnlichen Nebenwirkungen und Eigenschafteneinstellungen ab. Diese Schnittstellen sind jedoch klein: Teil 2 des Inhaltsverzeichnisses bietet bereits eine vollständige Zusammenfassung und Übersicht. Diese Reduzierung auf weniger als 20 Eigenschaften und 35 Methoden macht die vollständigen Funktionen des Canvas-Elements überschaubar und verständlich. Mit Hintergrundkenntnissen in HTML, CSS und zumindest ein wenig JavaScript kann dieses Canvas-Handbuch möglicherweise sowohl als Einführung als auch als täglicher Arbeitsbegleiter dienen, der ausreicht, um das gesamte Thema zu beherrschen.

Überblick

Dieses Handbuch besteht aus drei Teilen:

1. Einleitung
Eine erste Bekanntschaft mit dem Canvas-Element, einfachen Beispielen und der Grundeinstellung.
2. Referenz
Dieser größte Teil des Canvas-Handbuchs ist eine umfassende Referenz für die beiden wichtigsten beteiligten JavaScript-Objekte: 1. the HTMLCanvasElementund 2. the CanvasRenderingContext2D. Stellen Sie sich diesen zweiten Teil als benutzerfreundliche Version des offiziellen [W3C] -Standards für das Canvas-Element vor. Die Eigenschaften und Methoden sind wie in der Referenzzusammenfassung dieses Dokuments angegeben strukturiert .
3. Anhänge
für einige Elemente von CSS , HTML5 Dateivorlagen und Notizen über zusätzliche Werkzeuge und Werkzeugbau .

Wenn Sie ein Anfänger sind, lesen Sie den ersten Teil und durchsuchen Sie dann den zweiten Teil nach den Dingen, die Sie benötigen.

Online-Ressourcen zu diesem Text
[HOME] 1
für (die neueste Version von) diesem Text.
[FORUM] 2
ist eine interaktive Seite für Benutzer- und Autorenkommentare zu diesem Text und Thema.
Danksagung

Es gibt nette Online-Ressourcen, die mir die ersten Schritte beigebracht haben, insbesondere das Canvas-Tutorial des Mozilla Developer Network .

Dieses Handbuch selbst wurde in Markdown geschrieben und mithilfe des wunderbaren universellen Dokumentenkonverters Pandoc in HTML konvertiert. 3

1 Einleitung

1.1 Offizielle Standards

Dies sind die offiziellen Papiere, die das Canvas-Element beschreiben und definieren:

[W3C] 4
Das W3C ( World Wide Web Consortium ) definiert den HTML5-Standard. In diesem Abschnitt wird das Canvas-Element beschrieben.
[WAS] 5
ist der Abschnitt des Entwurfs der Spezifikation für Webanwendungen der WHATWG ( Arbeitsgruppe für Web-Hypertext-Anwendungstechnologie ), in dem das Canvas-Element ursprünglich standardisiert ist.
[W3C / 2D] 6
ist die W3C-Beschreibung des HTML Canvas 2D-Kontexts

1.2 Anforderungen: Ein Browser mit Unterstützung für HTML5 und JavaScript

Das Canvas-Element ist Teil von HTML5 . Wenn ein Browser zu alt ist oder nicht in der Lage ist, mit diesem relativ neuen Standard umzugehen, können Sie ihn überhaupt nicht verwenden.

Eine detaillierte Übersicht über die Browserunterstützung für HTML5 im Allgemeinen und das Canvas-Element im Besonderen finden Sie beispielsweise unter caniuse.com .

Darüber hinaus muss Ihr Browser in der Lage und berechtigt sein, JavaScript-Code auszuführen. Vor ein paar Jahren hatte JavaScript den Ruf, den Benutzer zu ärgern, und in vielen Konfigurationen war es standardmäßig ausgeschaltet. Aber all dies gehört der Vergangenheit an, ganz sicher, wenn Sie nicht alle Funktionen von HTML5 und insbesondere das Canvas-Element verpassen möchten.

Um diesen Text zu verstehen, stellen Sie sicher, dass Ihr Browser auf dem neuesten Stand und ordnungsgemäß konfiguriert ist (siehe das erste Beispiel als Test, um sicherzustellen, dass er ordnungsgemäß funktioniert). Alle Abbildungen und Beispiele werden nicht als <img>Bilder eingefügt , sondern mit Canvas-Elementen.

1.3 Ein erstes Beispiel

Kleine Grußbox

Hier ist ein Beispiel für ein Leinwandbild, das eine "Hallo" -Nachricht in einem roten Feld zeigt: 7

Gehen Sie und aktualisieren Sie Ihren Browser, bevor Sie weiterlesen!

Der Quellcode für dieses Bild lautet: 8

<canvas id="VeryFirstExampleCanvas" width=150 height=60>
  GO AND UPDATE YOUR BROWSER BEFORE YOU READ ON!
</canvas>

<script>
  var canvas = document.getElementById ('VeryFirstExampleCanvas');    // access the canvas object
  var context = canvas.getContext ('2d');                             // access the canvas context
  context.fillStyle = 'red';                                          // set the color to 'red'
  context.fillRect (0,0,150,60);                                      // draw a red box on the whole of the canvas
  context.fillStyle = 'yellow';                                       // set the color to 'yellow'
  context.fillRect (5,5,140,50);                                      // draw a yellow box inside the red one
  context.fillStyle = 'red';                                          // set the color back to 'red'
  context.font = '40pt Arial';                                        // define the CSS font for writing text
  context.fillText ('Hello',10,50);                                   // write the text 'Hello'
</script>

Dieses kleine Beispiel enthält bereits alle Grundzutaten und Schritte, die wir später immer wieder wiederholen werden. Jedes normale Leinwandbild besteht aus zwei Teilen des Codes:

  1. Erstens gibt es den <canvas>...</canvas>Teil, der Teil der HTML-Datei an der Stelle ist, an der sich das Bild befinden soll.
    Es hat die beiden Attribute für widthund length, die die Größe des rechteckigen Bildes definieren, gemessen in Pixel. In diesem Beispiel ist es 150pixelweit und 60pixelhoch.
    Der Text innerhalb des <canvas>...</canvas>Tags ist nicht der "Inhalt" der Zeichenfläche, sondern nur eine sogenannte "Fallback" -Nachricht. Dies sollte der Benutzer sehen, wenn der Browser zu alt ist oder nicht in der Lage ist, mit Canvas-Elementen umzugehen.

  2. Der zweite Teil des Codes ist das JavaScript-Skript. In diesem Beispiel wird es direkt unter und innerhalb eines <script>...</script>Tags platziert. Tatsächlich ist der Speicherort dieses Codeteils jedoch ziemlich willkürlich und wird häufig in eine separate .jsDatei verschoben . Das Skript führt normalerweise drei Schritte aus:

    1. Laden Sie das HTML- <canvas>Tag in ein HTMLCanvasElementObjekt. Wir haben es in einer Variablen gespeichert, die canvasdurch Aufrufen der ersten Zeile benannt wurde

      var canvas = document.getElementById('VeryFirstExampleCanvas');
    2. Das Bild wird nicht direkt im HTMLCanvasElement, sondern in seinem sogenannten Kontext erzeugt , einem internen CanvasRenderingContext2DObjekt, das wir in einer anderen Variablen gespeichert haben, die contextdurch Aufrufen der zweiten Zeile benannt wurde

      var context = canvas.getContext('2d');
    3. Jetzt ist alles so eingestellt, dass "Sachen gezeichnet" werden, indem Eigenschaften geändert und Methoden aufgerufen werden, nämlich:

      context.fillStyle = 'red';          // set the color to 'red'
      context.fillRect (0,0,150,60);      // draw a red box on the whole of the canvas
      context.fillStyle = 'yellow';       // set the color to 'yellow'
      context.fillRect (5,5,140,50);      // draw a yellow box inside the red on
      context.fillStyle = 'red';          // set the color back to 'red'
      context.font = '40pt Arial';        // define the CSS font for writing text
      context.fillText ('Hello',10,50);   // write the text 'Hello'

    Der Kontext ist der Ort, an dem der Bildinhalt generiert wird und die Beschreibung seiner Eigenschaften und Methoden den größten Teil dieses Handbuchs ausmacht. Um jedoch mit dem Canvas-Element arbeiten zu können, müssen wir diese grundlegenden Schritte verstehen.

Die Trikolore

Lassen Sie uns die gleichen Schritte noch einmal ausführen. Diesmal erzeugen wir die Nationalflagge Frankreichs .

Ihr Browser funktioniert immer noch nicht!

Der Quellcode lautet wie folgt:

<canvas id="LittleTricolour" width=120 height=90> Your browser still doesn't work! </canvas>
<script>
  // First, get hold of the canvas object and its context
  var tricolourCanvas = document.getElementById('LittleTricolour');    // access the canvas object
  var tricolourCanvasContext = tricolourCanvas.getContext('2d');       // access the canvas context
  // Now do the real drawings:
  tricolourCanvasContext.fillStyle = '#0055A4';    // set the color to blue
  tricolourCanvasContext.fillRect ( 0, 0, 40, 90); // draw a blue rectangle on the left
  tricolourCanvasContext.fillStyle = '#FFFFFF';    // set the color to white
  tricolourCanvasContext.fillRect (40, 0, 40, 90); // draw a white rectangle in the middle
  tricolourCanvasContext.fillStyle = '#EF4135';    // set the color to red
  tricolourCanvasContext.fillRect (80, 0, 40, 90); // draw a red rectangle on the right    
</script>

Alle visuellen Bildelemente werden ausschließlich im Canvas-Kontext generiert und in der willkürlich benannten JavaScript-Variablen gespeichert tricolourCanvasContext.

1.4 Grundeinstellung

Jedes Leinwandelement existiert in zwei Welten und zwei Formen:

Diese doppelte Natur des HTML-Tags einerseits und des JavaScript-Objekts andererseits ist nicht spezifisch für das Canvas-Element, sondern wesentlich für das gesamte Design des DOM (Document Object Model) 9 , das die dynamische Bearbeitung eines Dokuments mittels ermöglicht JavaScript.

Das <canvas>Tag

In gewisser Weise <canvas>ähnelt das <img>Tag dem Tag zum Anzeigen von Bildern. Zum Beispiel der Quellcode

<img src="horse.jpg" width=100 height=100 alt="SOMETHING GOES WRONG" style="border: solid 5pt orange"> </img>

generiert das folgende Bild in Ihrem Browser

ETWAS LÄUFT SCHIEF

Mit widthund stellen heightwir die Größe des Bildes ein. (Denken Sie daran, dass dies jeweils 100eine Abkürzung für ist "100px".) Mit altbieten wir einen "alternativen" oder Fallback-Text an, der angezeigt wird, falls das Bild aus irgendeinem Grund nicht angezeigt werden kann. Das allgemeine styleAttribut für HTML-Elemente wird verwendet, um einige CSS-Attribute festzulegen. In diesem Fall wird ein orangefarbener Rand um das Bild gezogen. Und natürlich hätten wir das schließende </img>Tag weglassen können .

Das <canvas>Tag hat ähnliche Funktionen. Zum Beispiel diese Codezeile

<canvas width=100 height=100 style="border: solid 5pt orange"> SOMETHING GOES WRONG </canvas>

erscheint so

ETWAS LÄUFT SCHIEF

Wiederum widthund heightgesetzt sind dies tatsächlich die einzigen zwei eigenen Attribute des <canvas>Tags. Das styleAttribut ist ein allgemeines Attribut für HTML-Elemente und legt erneut einen orangefarbenen Rahmen um die Zeichenfläche. Wenn wir dieses Attribut weggelassen hätten, könnten wir die Leinwand überhaupt nicht richtig sehen.

Anders als das altAttribut für das <img>Tag befindet sich der Fallback-Inhalt für das <canvas>nicht in einem Attribut, sondern befindet sich innerhalb der <canvas>...</canvas>Tags.

Übrigens widthund heightkann tatsächlich weggelassen werden. In diesem Fall ist die Breite standardmäßig auf 300pxund die Höhe auf eingestellt 150px. Zum Beispiel,

<canvas style="border: solid 5pt orange"> </canvas>

erscheint so

Das styleAttribut ist eine allgemeine Option für alle HTML-Tags, um einige CSS-Funktionen hinzuzufügen. Tatsächlich ist dies jedoch nicht der Ort, an dem der tatsächliche Bildinhalt eines Canvas-Elements gezeichnet wird. Dies geschieht im sogenannten Canvas- Kontext .

Das Canvas-Objekt und sein 2d-Kontext

Der reale grafische Inhalt und die Bildelemente einer Leinwand befinden sich weder in Attributen noch zwischen dem <canvas>...</canvas>Tag. Es ist überhaupt nicht in HTML oder CSS geschrieben. Stattdessen erfolgt dies im sogenannten Canvas- Kontext , auf den nur in JavaScript zugegriffen und dieser geändert werden kann.

Es gibt verschiedene Möglichkeiten, ein Leinwandbild auf dem Bildschirm anzuzeigen. Tatsächlich kann dies sogar vollständig in JavaScript erfolgen, ohne dass ein <canvas>Tag erforderlich ist. Wir beabsichtigen jedoch nicht, zu tief in das JavaScript-DOM einzutauchen und stattdessen eine Standardmethode zu verbreiten, die in den meisten Fällen ausreichen sollte.

Die Standardmethode ist folgende: 10

Insgesamt ist dies also unsere Standardvorlage zum Generieren von Leinwandbildern, zumindest für den Moment:

<canvas id="MyCanvasNo123" width=150 height=75> </canvas>
<script>
  var canvas = document.getElementById ('MyCanvasNo123');
  var context = canvas.getContext ('2d');
  // ...
  // ... do stuff with the context ...
  // ...
</script>

Wir verwenden diese Vorlage in diesem Text und in vielen Beispielen zeigen wir nur den "... do stuff with the context ..."Teil, vorausgesetzt, Sie kennen die anderen Zeilen. In den meisten Beispielen funktioniert diese einfache Vorlage und ist ein ausreichender Ausgangspunkt für die Gestaltung Ihrer eigenen Beispieldateien und Gemälde.

Aber auf einer professionelleren Ebene und wenn die erzeugten Bilder komplexer werden, sollten Sie dieses Design ändern:

All diese Probleme werden im Abschnitt Vorlagen des Anhangs behandelt.

Übrigens gibt es andere Kontexttypen als den 2D-Kontext mit seinen CanvasRenderingContext2DObjekten. Das [W3C] enthält einen Hinweis: Eine zukünftige Version dieser Spezifikation wird wahrscheinlich einen 3D-Kontext definieren (wahrscheinlich basierend auf der OpenGL ES-API). All dies liegt jedoch außerhalb des Rahmens dieses Textes und wird von den großen Browsern noch nicht allgemein unterstützt.

1,5 Punkte (x,y)und die Leinwandkoordinaten

Jede Leinwand hat ein widthund ein height, und standardmäßig sind dies 300bzw. 150Pixel. Dies bedeutet, dass die Leinwand in 300Pixel in die horizontale oder x-Achse und 150Pixel in die vertikale oder y-Achse unterteilt ist . Die x-Achse verläuft von links 0nach rechts 300, die y-Achse von oben 0nach unten 150. 11

Innerhalb dieses Koordinatensystems wird jeder Punkt auf der Leinwand durch das Zahlenpaar (x,y)mit 0 ? x ? widthund genau definiert 0 ? y ? height.

Beispielsweise wird der Punkt (200,25)auf einer 300x150Pixelgröße mit Standardgröße durch den roten Punkt 12 dargestellt

Da diese Position für den Leser jedoch schwer zu überprüfen ist, zeigen wir unten häufig ein Koordinatensystem oder ein Raster auf Leinwandbeispielen an. Der gleiche Punkt ist dann leicht zu finden:

Alle Punkte (x,y), die außerhalb des Bereichs von liegen 0 ? x ? widthund 0 ? y ? heightnicht angezeigt werden. Beispielsweise werden die Punkte (-60,50)oder (500,500)nicht in der vorherigen Zeichenfläche angezeigt. 13 Wenn (Teile von) Figuren die Ränder der Leinwand überschreiten, werden die überschüssigen Teile einfach abgeschnitten, wie bei der folgenden Scheibe mit Mittelpunkt (250,100)und Radius 100.

Ein anderes Beispiel

Wenn wir zeichnen, sagen wir die deutsche Flagge auf einem solchen Gitter

dann ist es einfacher, dem Code auf dem Bildergebnis zu folgen, nämlich: 14

context.fillStyle = '#000000';  // set the color to black ('#000000' is the same as 'black')
context.fillRect(0, 0,200,40);  // draw a rectangle field with top left point at (0,0), 200 pixel wide and 40 pixel high
context.fillStyle = '#FF0000';  // set the color to red (which is '#FF0000' is CSS hexadecimal color notation)
context.fillRect(0,40,200,40);  // draw same size (200x40 pixel) rectangle at (0,40)
context.fillStyle = '#FFCC00';  // set the color to gold
context.fillRect(0,80,200,40);  // last rectangle with left top point at (0,80)

Tatsächlich können wir dem ein solches Raster hinzufügen, contextindem wir die context.addGrid()Methode aufrufen. Dies kann sehr praktisch sein, wenn Sie Code schreiben. Sobald die gesamte Leinwand gezeichnet ist, können Sie diese Linie einfach wieder entfernen. Beachten Sie jedoch, dass dies addGrid()keine Standardmethode des CanvasRenderingContext2DObjekts ist! Wenn Sie es selbst verwenden möchten, müssen Sie die Implementierung aus dem Anhang in Ihre eigene Datei kopieren .

2 Referenz

2.1 HTMLCanvasElement

Das Canvas-Element wird in zwei Formen angegeben: als HTML-Tag und als JavaScript-Objekt . In dieser Referenz konzentrieren wir uns auf die JavaScript-Seite der Dinge. Der Vollständigkeit halber lassen Sie uns eine kurze Wiederholung der Grundeinstellung geben

Das <canvas>Tag

In HTML5 hat ein <canvas>Tag die allgemeine Form

<canvas id="...." width="..." height="...">
  ... fallback content ...
</canvas>

Tatsächlich hat das canvasElement nur zwei eigene Attribute widthund height. Beide sind optional. Wenn sie weggelassen werden , wird standardmäßig die Breite 300pxund die Höhe auf festgelegt 150px.

Das ... fallback content ...ist, was im Browser erscheint, der nicht HTML5-fähig ist. Da wir diese Situation hier nicht berücksichtigen müssen, verwenden wir dies nur als Standardformular für unsere Beispiele:

<canvas id="..." width="..." height="..."> </canvas>

Das idAttribut ist nur ein allgemeines Attribut für das HTML-Element. Wir fügen es jedoch in den folgenden Beispielen hinzu, da es eine Standardmethode zum Abrufen des HTMLCanvasElementObjekts in JavaScript bietet . Normalerweise machen wir so etwas

<canvas id="mycanvas" width=100 height=100> </canvas>
<script>
  var canvas = document.getElementById ("mycanvas");
  // ... now, we have the canvas element available and can manipulate it ...
</script>

Die canvasVariable ist dann ein Handle für die HTMLCanvasElement.

Das HTMLCanvasElement

Das JavaScript-Canvas-Objekt ist a HTMLCanvasElement. Es hat:

Das Canvas-Objekt ist ziemlich einfach, die tatsächlichen Zeichnungen und Bildelemente werden in seinem Kontext generiert . Im Allgemeinen und in diesem Handbuch betrachten wir nur eine Art von Kontext, nämlich den 2D-Kontext , der CanvasRenderingContext2Dim zweiten Teil dieser Referenz unter der Überschrift umfassend dokumentiert ist .

2.1.1 width

HTMLCanvasElement.width

speichert die Breite des Canvas-Elements, wie durch das widthAttribut des <canvas>Tags angegeben. Es ist jedoch auch möglich, diesen Wert zu ändern. Standardmäßig hat eine Leinwand eine 300Pixelbreite.

Platzieren Sie beispielsweise ein <canvas>Tag mit nicht standardmäßiger Breite und Höhe und fügen Sie einen orangefarbenen Rand (über dessen styleAttribut) hinzu, damit dieser sichtbar ist. Dann fügen wir ein Skript hinzu, das diese Zeichenfläche verwendet, ihre widthund vorliest heightund das Ergebnis in die Zeichenfläche schreibt. Alle zusammen,

<canvas id="SizeReadingSample" width=456 height=123 style="border: 5pt solid orange"> </canvas>
<script>
  var canvas = document.getElementById('SizeReadingSample');
  var message = "This canvas is " + canvas.height + " pixel high and " + canvas.width + " pixel wide.";
  var context = canvas.getContext('2d');
  context.font = "20px Arial";
  context.fillText (message, 15, 70);
</script>  

ist der Code, der dieses Bild erzeugt

2.1.2 height

HTMLCanvasElement.height

speichert die Höhe des Canvas-Elements, wie durch das heightAttribut des <canvas>Tags angegeben, das 150standardmäßig ist.

Im vorherigen Beispiel haben wir das gesehen heightund widthsind lesbare Eigenschaften. Sie sind aber auch beschreibbar und wir können diese Werte dynamisch ändern.

Angenommen, wir haben eine <canvas>Standardgröße, dh 300Pixel breit und 150hoch, und wir fügen ihr über ihr styleAttribut erneut einen orangefarbenen Rand hinzu , um ihre Form zu visualisieren.

<canvas id="SizeChangingSample" width=300 height=150 style="border: 5pt solid orange"> </canvas>

Dann fügen wir ein Skript hinzu, in dem wir die Größe der Zeichenfläche explizit ändern, indem wir beide widthund heightnur 30Pixel festlegen, wie folgt:

var canvas = document.getElementById('SizeChangingSample');
canvas.width = 30;
canvas.height = 30;

Das Ergebnis ist in der Tat eine kleinere, verkleinerte Leinwand, nämlich:

2.1.3 getContext('2d')

HTMLCanvasElement.getContext (contextId)

Gibt den sogenannten Kontext zurück , dh ein Objekt, das eine API zum Zeichnen auf der Leinwand verfügbar macht. Derzeit wird nur das CanvasRenderingContext2DObjekt unterstützt, und dafür contextIdist das '2d'. Das Ergebnis dieses Aufrufs ist, nullwenn die angegebene Kontext-ID nicht unterstützt wird.

Dieser Kontext , der in der Fortsetzung immer der 2D-Kontext sein wird, wird an die Leinwand angehängt und ist durch einen Aufruf von zugänglich canvas.getContext('2d'). In diesem Kontext findet die gesamte Aktion statt. Es bietet die Schnittstelle für alle Zeichnungen, die der angegebenen Leinwand hinzugefügt wurden, und die Erläuterung des folgenden CanvasRenderingContext2DObjekts ist daher der größte Teil dieses Handbuchs.

Das [W3C] -Dokument kündigt an, dass es in Zukunft wahrscheinlich einen '3d'Kontext geben wird, der auf der OpenGL ES-API basiert.

2.1.4 toDataURL()

HTMLCanvasElement.toDataURL ()
HTMLCanvasElement.toDataURL (type,... )

Gibt eine data:URL für das Bild im Canvas zurück. Das erste optionale typeArgument steuert den Typ des zurückzugebenden Bildes (z. B. PNG oder JPEG). Der Standardtyp ist 'image/png'für PNG- Bilder. Die anderen Argumente sind typspezifisch und steuern die Art und Weise, wie das Bild generiert wird.

Betrachten wir ein sehr kleines Leinwandbeispiel, beispielsweise ein rotes 20 x 20 Pixel großes Rechteck

generiert durch den folgenden Code

<canvas id="DataUrlSample" width=20 height=20></canvas>
<script>
  var canvas = document.getElementById ('DataUrlSample');
  var context = canvas.getContext ('2d');
  context.fillStyle = 'red';
  context.fillRect (0, 0, 20, 20);  
</script>

Wir können jetzt die Zeichenfläche in eine PNG-Daten-URL konvertieren, indem wir Folgendes aufrufen:

var dataUrl = canvas.toDataURL();  

und dataUrlhält jetzt diese Zeichenfolge:

Daten: Bild / PNG;

Wenn Sie diese Zeichenfolge kopieren und in das Adressfeld Ihres Browsers einfügen, sollte das Bild des roten Quadrats angezeigt werden.

Wir können auch zu einem konvertieren JPEG wie so

var dataUrl = canvas.toDataURL('image/jpeg');  

und dann ist das Ergebnis diese Zeichenfolge:

Daten: image / JPEG; Base64, / 9j / 4AAQSkZJRgABAQAAAQABAAD / 2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD / 2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD / wAARCAAUABQDASIAAhEBAxEB / 8QAFQABAQAAAAAAAAAAAAAAAAAAAAj / xAAUEAEAAAAAAAAAAAAAAAAAAAAA / 8QAFgEBAQEAAAAAAAAAAAAAAAAAAAcJ / 8QAFBEBAAAAAAAAAAAAAAAAAAAAAP / aAAwDAQACEQMRAD8AnQBDGqYAAAAAD // Z

Sie können dies alles kopieren und in Ihrem Browser aufrufen, um das kleine rote Quadrat erneut zu erhalten.

Für eine bequemere Lösung können wir diese Zeilen an den vorherigen Code anhängen: 15

canvas.onclick = function () {
  window.location = canvas.toDataURL('image/png');
};

Wenn wir dann auf die Leinwand klicken, öffnet der Browser die Daten-URL in das Adressfeld und zeigt so das Bild. Anschließend kann der Benutzer dieses Bild als PNG-Datei speichern.

2.2 CanvasRenderingContext2D

2.2.1 Verweis zurück auf die Leinwand

2.2.1.1 canvas

CanvasRenderingContext2D.canvas

verweist auf den HTMLCanvasElementgegebenen Kontext.

Angenommen, wir haben den folgenden Code:

<canvas id="ReferenceSample1" width=150 height=100 style="border: solid 5pt orange"> </canvas>
<script>
  var context = document.getElementById('ReferenceSample1').getContext('2d');
  context.canvas.width = 250;        // reset the canvas width to 250
  context.canvas.height = 50;        // reset the canvas height to 50
</script>

Dies wird als Leinwand mit 250 x 50 Pixel angezeigt (sichtbar aufgrund des durchgehenden orangefarbenen 5-Punkt-Randes):

Die im <canvas>Tag angegebene Originalgröße der Leinwand beträgt 150 x 100 Pixel. Die JavaScript-Variable contextenthält das CanvasRenderingContext2DObjekt. Mit context.canvasbekommen wir das HTMLCanvasElement, was diesen Kontext enthält. Also mit context.canvas.widthund context.canvas.heightbeziehen wir uns auf die Breite und Höhe dieser Leinwand.

Natürlich hätten wir genau das gleiche Bild erhalten mit:

<canvas id="ReferenceSample2" width=150 height=100 style="border: solid 5pt orange"> </canvas>
<script>
  var canvas = document.getElementById('ReferenceSample2');
  var context = canvas.getContext('2d');
  canvas.width = 250; 
  canvas.height = 50;
</script>

Der Punkt hier war jedoch die Demonstration der Fähigkeit, aus seinem 2D-Kontext auf das Canvas-Objekt zu verweisen.

2.2.2 Zustand

Ein Zeichnungsstatus ist die Menge der aktuellen Einstellungen eines CanvasRenderingContext2DObjekts. Es enthält die aktuellen Stil - Werte ( strokeStyleund fillStyle), globalAlphaund globalCompositeOperationdie Leitung ( lineWidth, lineCap, lineJoin, miterLimit), Schatten ( shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor) und Texteinstellungen ( font, textAlign, textBaseline), den aktuellen Auswahlbereich und die aktuelle Transformationsmatrix .

Jedes CanvasRenderingContext2DObjekt verwaltet einen Stapel von Zeichnungszuständen . Mit save()und restore()können Zeichnungszustände auf diesen Stapel verschoben und zu einem späteren Zeitpunkt wiederhergestellt werden.

Beachten Sie, dass der Zeichnungsstatus weder den aktuellen Pfad noch die Bitmaps enthält. So save()und restore()nicht speichern und ganze Leinwand Bilder wiederherstellen, dh gesamten Inhalt des gegebenen Kontextes. Verwenden Sie dazu ImageDataObjekte und die Methoden getImageData()und putImageData()(siehe Kapitel zur Pixelmanipulation ).

Beispiel

Angenommen, wir zeichnen 5 Figuren, von denen jede ein fillRect()Rechteck mit zwei Textzeilen enthält strokeText():

Wie aus dem Bild ersichtlich ist, verwenden wir 3 verschiedene Stileinstellungen für die 5 Figuren, die als Zustände definiert sind :

Wir könnten die vorherige Zeichenfläche generieren, indem wir die folgenden Schritte ausführen:

  1. Deklarieren Sie Zustand A und zeichnen Sie Abbildung 1 und 5
  2. Deklarieren Sie Zustand B und zeichnen Sie Abbildung 2 und 4
  3. deklarieren Sie Zustand C und zeichnen Sie Abbildung 3

Da wir jedoch die Verwendung von save()und demonstrieren möchten restore(), erstellen wir die Figuren in der angegebenen Reihenfolge:

  1. Setzen Sie Status A , zeichnen Sie Abbildung 1 und speichern Sie Status A auf dem Statusstapel
  2. Setzen Sie Status B , zeichnen Sie Abbildung 2 und speichern Sie Status B auf dem Statusstapel
  3. Setze Zustand C und zeichne Abbildung 3
  4. Überschreiben Sie Zustand C, indem Sie Zustand B vom Stapel wiederherstellen , und zeichnen Sie Abbildung 4
  5. Stellen Sie den Zustand A wieder her und zeichnen Sie Abbildung 5

Der vollständige Quellcode, der die Zeichenfläche tatsächlich generiert hat, lautet:

// Text style settings (these will be part of Start A, B, and C alike, because they do not change) 
context.textAlign = 'center';
context.textBaseline = 'middle';
context.lineWidth = 2.0;
context.font = '25px Arial';    
// Settings for State A 
var verticalGrad = context.createLinearGradient (0,0,0,200);
verticalGrad.addColorStop (0,'red');
verticalGrad.addColorStop (1,'green');
context.fillStyle = verticalGrad;                                      
context.strokeStyle = 'yellow';
// Draw Figure 1 
context.fillRect (25,25,100,150);
context.strokeText ("Fig 1",75,50);
context.strokeText ("State A", 75,125);  
// Save State A
context.save();  
// Settings for State B
var radGrad = context.createRadialGradient (375,100,5, 375,100,200);
radGrad.addColorStop (0,'orange');
radGrad.addColorStop (1,'yellow');
context.fillStyle = radGrad;
context.strokeStyle = 'green';  
// Draw Figure 2
context.fillRect (175,25,100,150);
context.strokeText ("Fig 2",225,50);  
context.strokeText ("State B",225,125);  
// Save State B
context.save();  
// Settings for State C
context.fillStyle = '#888888';
context.strokeStyle = '#EEEEEE';  
// Draw Figure 3
context.fillRect (325,25,100,150);
context.strokeText ("Fig 3",375,50);  
context.strokeText ("State C",375,125);  
// Pop State C and restore State B
context.restore();  
// Draw Figure 4
context.fillRect (475,25,100,150);
context.strokeText ("Fig 4",525,50);  
context.strokeText ("State B",525,125);  
// Pop state B and restore state A
context.restore();  
// Draw Figure 5
context.fillRect (625,25,100,150);
context.strokeText ("Fig 5",675,50); 
context.strokeText ("State A",675,125);  

2.2.2.1 save()

CanvasRenderingContext2D.save()

Schieben Sie den aktuellen Status auf den Statusstapel.

Eine Beispielanwendung finden Sie im vorherigen Arbeitsbereich.

2.2.2.2 restore()

CanvasRenderingContext2D.restore()

Entfernen Sie den obersten Status aus dem Statusstapel und stellen Sie so den vorherigen Status wieder her, der auf den Statusstapel verschoben wurde.

Ein Beispiel finden Sie auf der vorherigen Leinwand.

2.2.3 Transformationen

In der allgemeinen Geometrie verwandelt eine Transformation ein bestimmtes Objekt in ein anderes Objekt, behält jedoch seine Struktur bei. Dinge mögen deformiert erscheinen, aber sie werden nicht zerbrochen oder zerstört. Die absolute Position von Punkten kann sich ändern, aber die relativen Positionen bleiben erhalten, benachbarte Punkte sind nach der Transformation immer noch Nachbarn.

Drei spezielle Transformationen können mit speziellen Methoden im Canvas-Kontext aufgerufen werden:

scale()Ändert die Größe der Leinwand, rotate()dreht sie um den Ursprung und translate()verschiebt sie an eine neue Position. Aber tatsächlich und anders als in den drei vorherigen Bildern vorgeschlagen, wird durch keine dieser Operationen die Leinwand als Objekt im Browserfenster verschoben, sondern ihr Koordinatensystem wird geändert. Im Folgenden zeigen wir anhand vieler Beispiele, was dies bedeutet.

Neben diesen drei speziellen Transformationen gibt es auch die allgemeinen transform()und setTransform()Methoden, die leistungsfähiger, aber auch schwieriger zu verstehen und anzuwenden sind. 16 Wir zeigen auch, dass jede Kombination von Transformationen (z. B. zuerst Skalierung, dann Rotation und schließlich wieder Skalierung) selbst eine Transformation ist und mit einem transform()Aufruf ausgeführt werden kann. Umgekehrt bedeutet dies auch, dass wir eine komplexe Transformation in einfachere Schritte zerlegen können.

2.2.3.1 scale (x, y)

CanvasRenderingContext2D.scale (x, y)

Setzt den Breitenskalierungsfaktor auf xund den Höhenfaktor auf y, sodass alle Größen der nachfolgend gezeichneten Objekte mit diesen Konstanten multipliziert werden. xund ysind (Gleitkomma-) Zahlen.

Bei einer Leinwand mit 200 x 200 Pixeln, die mit einem roten Rand und einem blauen Koordinatensystem (hier über dem roten Rand) dargestellt ist:

Wenn wir jetzt sagen scale(0.8,1.5), sagen wir , die horizontale x-Achse schrumpft um den Faktor 0.8und die vertikale y-Achse erstreckt sich um den Faktor 1.5. Das gleiche blaue Koordinatensystem auf dem roten Rand ist:

Das bedeutet, dass alle nach dem Aufruf von gezeichneten Bildelemente entsprechend scale(0.8,1.5)kleiner und höher sind. Beachten Sie jedoch, dass sich die Größe der Zeichenfläche nicht geändert hat. Das Bild im Browser ist immer noch alles, was innerhalb des roten Randes liegt.

Beispiel

Nehmen wir noch einmal dieselbe Leinwand mit 200 x 200 Pixel (der rote Rand wird mit dem zusätzlichen Attribut style="border: solid 3pt red"im <canvas>Tag generiert ). Wir fügen der Leinwand einen grünen Kreis hinzu context, was mit diesem Code gemacht wird

context.strokeStyle = 'green';        // set the color for the circle to 'green'
context.lineWidth = 5.0;              // set the lineWidth for the circle to 5.0
context.beginPath();                  // start a new path
context.arc (100,100,80,0,2*Math.PI); // a circle with center point (100,100) and radius 80
context.stroke();                     // draw the path; in this case only the circle

Das Browserbild dieser Leinwand ist dies

Aber wenn wir dem vorherigen Code die Zeile voranstellen

context.scale (0.8, 1.5);              // shrink the x-coordinates by 0.8, extend the y-coordinates by 1.5

dann wird das Bild dies sein

Die Breite wird um verringert 0.8, die Höhe des Kreises um den Faktor erhöht 1.5. Die Leinwand hat sich nicht in der Größe geändert, sie ist immer noch rechteckig mit 200 x 200 Pixel. Infolgedessen wird der Kreisteil, der den Leinwandrand überschreitet, abgeschnitten.

Beispiel

Zum Beispiel dieser Code

context.fillStyle = 'red';
context.strokeStyle = 'black';
context.font = '40px Arial';    
// 1. draw a rectangle with text
context.fillRect (50, 50, 150, 40);
context.strokeText ('1. Hello', 55, 85);    
// 2. scale and draw the same rectangle with text again
context.scale (0.8, 2); 
context.fillRect (50, 50, 150, 40);
context.strokeText ('2. Hello', 55, 85);    
// 3. scale once more, and make the same drawings
context.scale (0.8, 2); 
context.fillRect (50, 50, 150, 40);
context.strokeText ('3. Hello', 55, 85);    

erzeugt dieses Bild

Zuerst zeichnen wir durch Aufrufen ein Rechteck fillRect(50,50,150,40)und fügen eine Nachricht mit ein strokeText('1. Hello',55,85). Dann skalieren wir xnach 0.8und ynach 2und nennen dasselbe fillRect(50,50,150,40)noch einmal. Dieses zweite Rechteck wird eingefügt bei (0.8*50,2*50)= (40,100), ist 0.8*150= 120Pixel breit und 2*40= 80Pixel hoch. Das zweite skalierte Rechteck entspricht also tatsächlich einem Aufruf von fillRect(40,100,120,80)im ursprünglichen Koordinatensystem. Wenn wir jedoch die zweite Nachricht durch Aufrufen hinzufügen, erhalten strokeText('2. Hello',55,85)wir auch eine skalierte Nachricht. Und dafür brauchen wir wirklich die scale()Methode.

Negative Werte und Spiegelbilder

Wenn wir scale(x,y)mit einem Negativ aufrufen x, wird das neue Koordinatensystem auf der y-Achse gespiegelt. Und wenn ynegativ, wird das neue Koordinatensystem auf der x-Achse gespiegelt.

Angenommen, es gibt eine Leinwand mit 200 x 200 Pixeln (der rote Rahmen), dann scale(-1,1)lautet das (blaue) Koordinatensystem nach einem Aufruf von :

Wenn wir diese 200x200 Pixel Leinwand nehmen, dann dieses Skript

context.scale (-1, 1);
context.fillStyle = 'green';
context.fillRect (-175, 70, 150, 60);
context.fillStyle = 'yellow';
context.fillRect (-170, 75, 140, 50);
context.fillStyle = 'green';
context.font = "40pt Arial";
context.fillText("Hello", -160, 120);

produziert dieses Bild:

Beachten Sie, dass die x-Koordinaten aller Punkte innerhalb der Leinwand jetzt negativ sind. Zum Beispiel platzieren wir den Text bei (-160,120)und er läuft von rechts nach links.

Wenn wir dagegen aufrufen scale(1,-1), erhalten wir ein Bild, das auf der x-Achse gespiegelt ist:

2.2.3.2 rotate (angle)

CanvasRenderingContext2D.rotate (angle)

bewirkt eine angleDrehung aller nachfolgend gezeichneten Objekte angleals (Gleitkomma-) Zahl.

Denken Sie daran, dass Winkel in diesem Zusammenhang immer im Bogenmaß statt im Grad definiert werden. Wenn Sie eine Gradrotation benötigen d, können Sie anrufen

rotate (d * Math.PI / 180);

Angenommen, wir haben eine rot dargestellte Leinwand

Wenn wir aufrufen rotate(angle), werden alle nachfolgend gezeichneten Objekte anglegemäß dem neuen blauen Koordinationssystem um das Gegebene gedreht . Beachten Sie jedoch, dass die gerenderte Leinwand im Browser immer noch die rote und nicht die gedrehte blaue Leinwand ist. Nur die Punkte, die innerhalb dieser blauen Leinwand liegen, sind sichtbar.

Betrachten wir genauer die Leinwandgröße mit Standardgröße (300 x 150 Pixel), die 15durch Aufrufen um Grad gedreht wird rotate(15*Math.PI/180).

Dies ist ein Bild dieser Leinwand vor der Drehung mit einem gelben Gitter und den Kreuzungspunkten wie (50,50)ausgeschrieben

Nach der 15Gradumdrehung sieht das gleiche Gitter so aus

Zum Beispiel ist der Punkt (250,100)verschwunden, weil er jetzt außerhalb der Leinwand liegt, während er (250,-50)jetzt existiert.

Beispiel

Dieses Code-Snippet wendet zwei aufeinanderfolgende Umdrehungen an

context.fillStyle = 'red';
context.strokeStyle = 'black';
context.font = '40px Arial';
// 1. draw a rectangle with text
context.fillRect (100, 5, 150, 40);
context.strokeText ('1. Hello', 105, 40);
// 2. declare a rotation and draw the same rectangle with text again
context.rotate (30 * Math.PI / 180);                                      // first rotation of 30 degree 
context.fillRect (100, 5, 150, 40);
context.strokeText ('2. Hello', 105, 40);
// 3. declare the same rotation, once more, and make the same drawings
context.rotate (30 * Math.PI / 180);                                      // second rotation of 30 degree 
context.fillRect (100, 5, 150, 40);
context.strokeText ('3. Hello', 105, 40);

Das erzeugte Bild ist dies

Zuerst zeichnen wir das obere horizontale Rechteck mit einer "Hello"Innenseite. Wir rotatezeichnen dann um 30 Grad und zeichnen wieder das gleiche Rechteck und den gleichen Text. Beachten Sie, dass die Koordinaten gleich sind, d fillRect(100,5,100,40). H. Das zweite Rechteck wird unter dem ersten gedreht angezeigt. Wir machen dann eine zweite Drehung um 30 Grad, gefolgt von einer dritten Zeichnung des Rechtecks ??und des Textes. Dadurch wird das dritte Rechteck erzeugt, das nur zur Hälfte angezeigt wird, da sich die andere Hälfte bereits außerhalb der Leinwand befindet.

2.2.3.3 translate (x, y)

CanvasRenderingContext2D.translate (x, y)

Verschiebt den Ursprung der Leinwandpixel xnach rechts und die yPixel nach unten. Jedes neue Element nach dem Aufruf von translate(x,y)wird gemäß diesen neuen Koordinaten hinzugefügt.

Angenommen, wir haben eine Leinwand mit einem 250x150Pixel, die durch den roten Rahmen und das blaue Koordinatensystem darüber angezeigt wird

Nach einem Aufruf von translate (100,-50)ist dies das neue Koordinatensystem

Im ursprünglichen Zustand und vor der Übersetzung hat die linke obere Ecke die Koordinaten (x,y)=(0,0)

Nach der Übersetzung befindet sich die linke obere Ecke der Leinwand ad (-100,50)

Alles, was wir sagen (200,100), erscheint also vor, aber nicht mehr nach der Übersetzung.

Beispiel

Dieser Code-Ausschnitt

context.fillStyle = 'red';
context.strokeStyle = 'black';
context.font = '40px Arial';
// 1. draw the first red rectangle with text  
context.fillRect (0,5,150,40);  
context.strokeText ('1. Hello', 5, 40);
// 2. declare a translation and draw the same rectangle, again
context.translate(125,50);                                                 // first call of translate(125,50)
context.fillRect (0,5,150,40);
context.strokeText ('2. Hello', 5, 40);
// 3. declare the same translation, once more, and make the same drawings
context.translate(125,50);                                                 // second call of translate(125,50)
context.fillRect (0,5,150,40);
context.strokeText ('3.Hello', 5, 40);  

erzeugt dieses Bild

Zuerst wird durch Aufrufen ein Rechteck gezeichnet fillRect(0,5,150,40)und Text mit hinzugefügt strokeText('1. Hello',5,40). Dann translate(125,50)wird aufgerufen und das gleiche Rechteck erneut mit gezeichnet fillRect(0,5,150,40). Da sich der Ursprung verschoben hat, wird dieses zweite Rechteck ebenfalls um 125 Pixel nach rechts und um 50 Pixel nach unten verschoben. Schließlich translate(125,50)wird noch einmal aufgerufen, wodurch sich der Ursprung wieder bewegt. Und wenn das Rechteck auch wieder mit gezeichnet wird fillRect(0,5,150,40), erscheint rechts und unter den beiden vorherigen ein drittes Rechteck. Beachten Sie, dass der gesamte Inhalt außerhalb der Leinwand abgeschnitten ist und das dritte Rechteck nicht vollständig angezeigt wird.

2.2.3.4 transform (a, b, c, d, e, f)

CanvasRenderingContext2D.transform (a, b, c, d, e, f)
ist die allgemeine Methode zur Durchführung einer affinen Transformation. Jeder Punkt (x,y)wird in den neuen Punkt umgewandelt (x',y'), wo

x' = a*x + c*y + e
y' = b*x + d*y + f

Die Auswirkung der einzelnen Parameter auf das Ergebnis ist wie folgt:

Parameter Bewirken
a skaliert horizontal (und spiegelt auf der y-Achse für negativ a)
b horizontal schief
c Schrägstellung vertikal
d skaliert vertikal (und Spiegel auf der x-Achse für negativ a)
e bewegt sich horizontal
f vertikal bewegen
Beispiel

Betrachten Sie eine Leinwand mit Standardgröße (dh 300 x 150 Pixel), die durch einen roten Rahmen mit einem blauen Koordinatengitter oben angegeben wird:

Nach einer Transformation von sagen

transform (0.5, 0.4, -0.2, 1.2, 200, 0)

Das neue Koordinatensystem ist dies

Und wenn wir diese Zeichenfläche contextvor und nach derselben Transformation füllen, indem wir diesen Code ausführen

// prepare the settings for color and font styles
context.fillStyle = 'red';
context.strokeStyle = 'black';
context.font = '40px Arial';
// 1. draw the first red rectangle with text  
context.fillRect (0, 5, 150, 40);  
context.strokeText ('1. Hello', 5, 40);
// 2. declare the translation and draw the same rectangle, again
context.transform (0.5, 0.4, -0.2, 1.2, 200, 0);
context.fillRect (0, 5, 150, 40);
context.strokeText ('2. Hello', 5, 40);

dann ist dies das resultierende Bild:

scale(), rotate()Und translate()in Bezug auftransform()

transform()zusammen mit dem ähnlichen setTransform()ist die allgemeine Methode, um (affine) Transformationen durchzuführen. scale(), rotate()Und translate()sind nur spezielle Versionen transform()mit weniger Parametern. Die folgende Tabelle zeigt die genauen Implementierungen:

besondere Transformation gleiche Transformation in Bezug auf transform()
scale(x,y) transform (x, 0, 0, y, 0, 0)
rotate(angle) transform (c, s, -s, c, 0, 0)
wo c = Math.cos(angle)unds = Math.sin(angle)
translate(x,y) transform (1, 0, 0, 1, x, y)

Also anstatt anzurufen sagen

context.scale (0.8, 1.5);

Den gleichen Effekt erzielen wir mit einem Aufruf von

context.transform (0.8, 0, 0, 1.5, 0, 0);

Bestimmte spezielle Rotationen sind diese:

Drehung bezüglich rotate() gleiche Transformation in Bezug auf transform()
90 Grad rotate(Math.PI/2) translate(0,1,-1,0,0,0)
180 Grad rotate(Math.PI) translate(-1,0,0,-1,0,0)
270 Grad rotate(1.5*Math.PI) translate(0,-1,1,0,0,0)
Die shear()Transformation

Eine weitere spezielle Transformation, die in der CanvasRenderingContext2DAPI nicht vordefiniert ist, ist

shear(x,y)

Das ist einfach eine Abkürzung 17 für

transform (1, y, x, 1, 0, 0)

Zum Beispiel,

2.2.3.5 setTransform (a, b, c, d, e, f)

CanvasRenderingContext2D.setTransform (a, b, c, d, e, f)
funktioniert wie transform(), aber anstatt die aktuelle Transformation zu transformieren , wird sie bei der anfänglichen Identitätstransformation durchgeführt .

Anfänglich hat jeder Canvas-Kontext das anfängliche Koordinatensystem , wie in der Einführung beschrieben . Nach jeder Transformation wird dieses Koordinatensystem in ein neues aktuelles System geändert . Bei jedem tranform()Aufruf wird dieses aktuelle System erneut transformiert. Ein Aufruf von setTransform()transformiert jedoch nicht das aktuelle System, sondern beginnt mit dem ersten.

Im Standardjargon sprechen wir eher wieder von "Transformation" als von "Koordinatensystem", daher gibt es eine anfängliche Transformation (nämlich die Transformation der definierten Identität ) und eine aktuelle Transformation .

Wie Transformationen im Canvas-Kontext funktionieren

Beginnen wir noch einmal mit einem breiteren Blick darauf, wie alles miteinander verbunden und umgesetzt ist. Es könnte hilfreich oder hinderlich sein, die zugrunde liegende Mathematik zu erklären, daher haben wir dies als optionale Information in einen kleinen Exkurs und seine Fußnoten isoliert.

Exkurs: Die Algebra der (affinen) Transformationen

Eine Transformation ? wird eindeutig bestimmt und repräsentiert 18 durch sechs numerische Parameter (a,b,c,d,e,f)und definiert eine Funktion, dass die Punkte gegeben Karten (x,y)an neue Punkte (x',y') = ?(x,y)durch und wo das Mapping definiert ist x' = a*x + c*y + eund y' = b*x + d*y + f.

Wenn und zwei Transformationen sind, wird ihre Zusammensetzung als die Funktion definiert, die jeden Punkt dem neuen Punkt zuordnet . Es stellt sich heraus, dass die Kompositionsfunktion wieder eine genau definierte Transformation ist. Transformationen werden unter Komposition geschlossen.?1?2 ?1??2(x,y)?2(?1(x,y))?1??2

Genauer gesagt bedeutet dies, dass es auch bei Parametertupeln eine genau definierte Zusammensetzung gibt ? . 19
If ist eine Darstellung für die Transformation und für und if(a1,b1,c1,d1,e1,f1)?1(a2,b2,c2,d2,e2,f2)?2

(a1,b1,c1,d1,e1,f1) ? (a2,b2,c2,d2,e2,f2) = (a3,b3,c3,d3,e3,f3)

dann ist die (einzigartige und genau definierte) Darstellung von .(a3,b3,c3,d3,e3,f3)?1??2

Die Identitätstransformation ist die Transformation id, die jeden Punkt (x,y)auf sich selbst abbildet id(x,y)=(x,y). Dies impliziert sofort, dass id ? ?= ? ? id= ?für jede Transformation ?.

Es ist leicht zu überprüfen, ob die Parameterdarstellung von idgegeben ist durch (1,0,0,1,0,0).

Zu jedem Zeitpunkt, unmittelbar nach seiner Erstellung, verfügt der Canvas-Kontext über eine Eigenschaft, die als aktuelle Transformation bezeichnet wird . Hier kann es hilfreich sein, "aktuelles Koordinatensystem" anstelle von "aktuelle Transformation" zu lesen. Und das macht wirklich Sinn: Wenn die Zeichenfläche im Browser gerendert wird, werden die Bildkomponenten gemäß der angegebenen aktuellen Transformation oder dem angegebenen _Koordinatensystem im Zeichenbereich platziert.

Im [WHATWG] -Standard hatte jeder CanvasRenderingContext2ddiese aktuelle Transformation noch explizit als currentTransformEigenschaft verfügbar . 20 Im endgültigen Standard von [W3C] ist diese Eigenschaft jedoch verschwunden und nicht mehr zugänglich.

Das anfängliche Koordinatensystem, das zum Zeitpunkt der Kontexterstellung, ist dasjenige mit dem Ursprung (0,0)am linken oberen Eckpunkt und der orthogonalen x- und y-Achse, wie in der Einleitung beschrieben . Die anfängliche Transformation ist tatsächlich die Identitätstransformation id .

Jedes Mal, wenn eine neue Transformation ?'wird durch einen im Skript ausgegeben transform()Anruf (oder eine ihrer speziellen Versionen scale(), rotate()oder translate()), die aktuelle Transformation ?wird durch die neue aktuelle Transformation ersetzt ???'. Und dieser Schritt wird nach jeder neuen Transformation wiederholt.

Und bei einem Aufruf von setTransform()wird die aktuelle Transformation nicht auf ???', sondern auf die neue Transformation ?'selbst gesetzt (wie sie durch die sechs Parameter des setTransform(a,b,c,d,e,f)Aufrufs definiert ist).

Der [WHATWG] -Standard kannte auch eine resetTransform()Methode, mit der die aktuelle Transformation explizit auf die Identität gesetzt wurde id. Dies wurde jedoch wieder aus der endgültigen [W3C] -Version entfernt. Der gleiche Effekt eines resetTransform()Anrufs kann natürlich auch mit einem setTransform(1,0,0,1,0,0)Anruf erzielt werden , wodurch auch die aktuelle Transformation auf die Identität zurückgesetzt wird id.

Beispiel (vertikaler Text)

Angenommen, wir möchten "HALLO!" auf einer Leinwand, aber in einem vertikalen Textfeld, wie so

Ein ähnliches Problem ist folgendes: Wie drehen wir beispielsweise das Koordinatensystem einer Leinwand?

auf die linke Seite

Die Antwort auf das letztere Problem lautet: Drehen Sie zunächst 270durch Aufrufen nach Gradrotate(1.5*Math.PI)

und zweitens, das Gitter nach unten bewegen , der x-Koordinatenachse , so daß (0,0)an der linken unteren Ecke befindet, durch Aufrufen translate(-h,0), mit hder Höhe der Leinwand ist, hier 150.

Zurück zum ursprünglichen Problem des vertikalen Textes haben wir also eine 300x255Pixel-Leinwand und rufen dann auf

context.rotate (1.5*Math.PI);  // rotation of 270 degree
context.translate (-255,0);    // move the coordinate system down along the x-axis by 255 pixel

was uns gibt

Wir zeichnen dann das Textfeld durch Aufrufen

context.fillStyle = 'red';
context.fillRect (0, 0, 255, 120);
context.fillStyle = 'yellow';
context.fillRect (5, 5, 245, 110);
context.fillStyle = 'red';
context.font = "50pt Arial";
context.lineWidth = 5.0;
context.fillText ("HELLO!", 10, 85);

so dass das vollständige Ergebnis wie erwartet ist:

Beispiel (Zusammensetzung der Transformationen)

Wir haben gerade eine Rotation und eine Übersetzung durchgeführt, können aber auch die beiden Transformationsschritte zu einem einzigen zusammenführen.

Erinnere dich daran

Die Zusammensetzung 21 der beiden vorhergehenden Übersetzungen ist
(0,-1,1,0,0,0) ? (1,0,0,1,h,0) = (0,-1,1,0,h,0)

Alles in allem bedeutet das das

context.rotate(1.5*Math.PI);
context.translate(h,0);

ist das gleiche wie

context.transform(0,-1,1,0,0,0);
context.transform(1,0,0,1,h,0);

und das hat den gleichen Effekt wie die Zusammensetzung

context.transform(0,-1,1,0,h,0);
Beispiel (Spiegelbild und Komposition mit Reset)

Fahren wir mit dem vorherigen Beispiel des vertikalen Textfelds fort. Es war noch etwas Platz auf der Leinwand und wir werden dort ein Spiegelbild einfügen. Dies soll das Ergebnis sein:

Das Bild ist ein Spiegelbild, da das Pferd im Originalbild auf die andere Seite schaut:

In der Erklärung von haben scale()wir bereits erwähnt, dass ein Spiegelbild auf der y-Achse durch einen scale(-1,1)Aufruf erreicht werden kann. Wir lassen darauf einen translate(-w,0)Aufruf folgen , wobei die Breite w= ist 300, so dass die Gesamttransformation 22 nun durch dieses Koordinatensystem gegeben ist

In diesem transformierten Canvas-Kontext fügen wir das Bild nun (0,0)mit einer zugewiesenen Breite 180und Höhe des 255Pixels ein. Der Code ist dies

context.scale (-1, 1);
context.translate(-300,0);
var image = new Image();
image.src = "horse.jpg";
context.drawImage (image, 0, 0, 180, 255);

und dies ist das resultierende Browserbild

Inzwischen haben wir also den Code für die beiden Teile des endgültigen Bildes

Erstens gibt es links den Text, der von generiert wird

// 1. The vertical text box 
// 1.a) Two transformations: first a rotation, then a translation
context.rotate (1.5 * Math.PI);
context.translate (-255,0);
// 1.b) The code for the text box
context.fillStyle = 'red';
context.fillRect (0, 0, 255, 120);
context.fillStyle = 'yellow';
context.fillRect (5, 5, 245, 110);
context.fillStyle = 'red';
context.font = "50pt Arial";
context.lineWidth = 5.0;
context.fillText ("HELLO!", 10, 85);

und dann ist da noch das Bild rechts, generiert von

// 2. The mirror image of the horse
// 2.a) Two transformations: first mirror at the y-axis, then a translation
context.scale (-1, 1);
context.translate (-300,0);
// 2.b) Insert the image
var image = new Image();
image.src = "horse.jpg";
context.drawImage (image, 0, 0, 180, 255);

Wenn wir jedoch beide Codefragmente addieren, erhalten wir dieses unerwartete Ergebnis:

Der Grund ist, dass die beiden Transformationen für das Bild für die beiden Transformationen für das Textfeld ausgeführt werden. Mit anderen Worten, ruft der transform(), scale(), rotate()und translate()die ändern aktuelle Transformation , die von der Standardanfang verschieden sein kann Identitätstransformation . Was wir vor dem zweiten Teil tun müssen, ist ein Zurücksetzen der aktuellen Transformation in die Identitätstransformation. Wie bereits erwähnt, musste das CanvasRenderingContext2DObjekt resetTransform()genau das tun. Im endgültigen Standard gibt es jedoch nur noch die setTransform()Methode. Denken Sie daran, dass die Identitätstransformation durch die Parameter gegeben (1,0,0,1,0,0)ist. setTransform(1,0,0,1,0,0)Wenn Sie also aufrufen, werden die aktuellen Transformationsparameter zurückgesetzt. Der Code für die gesamte Leinwand ist gegeben durch

// 1. The vertical text box 
// ... 
// Reset the current transformation
context.setTransform (1,0,0,1,0,0);
// 2. The mirror image of the horse
// ... 

2.2.4 Compositing

2.2.4.1 globalAlpha

CanvasRenderingContext2D.globalAlpha

bestimmt den aktuellen ?- Wert ( Alpha oder Transparenz ) für die Zeichnungen. Dieser ?-Wert ist eine Gleitkommazahl zwischen 0.0(für vollständig transparent) und 1.1(für vollständig undurchsichtig). Der Standardwert ist 1.0, was bedeutet, dass alle gezeichneten Elemente überhaupt nicht transparent sind.

Lassen Sie uns den Effekt auf einer Leinwand mit vier Rechtecken demonstrieren, der durch die folgende Codevorlage generiert wird:

<canvas id="GlobalAlphaSample" width=470 height=180 style="border: solid 1pt blue"> </canvas>
<script>
  var context = document.getElementById('GlobalAlphaSample').getContext('2d');
  // 1. setting alpha
  context.globalAlpha = "...";     // THIS IS THE IMPORTANT LINE!!!!!!!!!!!!!!!!!!!!
  // 2. set the text style
  context.font = '20px Arial'; context.strokeStyle = 'black';  
  // 3. draw the four rectangles
  context.fillStyle = 'red';     context.fillRect( 10,10,150,100);  context.strokeText('red',    20, 50);
  context.fillStyle = 'green';   context.fillRect(110,30,150,100);  context.strokeText('green', 120, 80);
  context.fillStyle = 'yellow';  context.fillRect(210,50,150,100);  context.strokeText('yellow',220,110);
  context.fillStyle = 'blue';    context.fillRect(310,70,150,100);  context.strokeText('blue',  320,140);
</script>

wobei wir jedes Mal einen anderen ?-Wert für das "..."Teil einfügen .

Die Einstellung context.globalAlpha = 1.0erzeugt Folgendes :

Natürlich ist dieser Wert 1.0der Standardwert für ?, daher hätten wir die gesamte Codezeile weglassen und trotzdem das gleiche Ergebnis erzielen können.

Die Einstellung context.globalAlpha = 0.5gibt uns jedoch jetzt Folgendes:

Und schließlich context.globalAlpha = 0.1:

2.2.4.2 globalCompositeOperation

CanvasRenderingContext2D.globalCompositeOperation

legt fest, wie ein neues (" Quell ") Bild gegen ein vorhandenes (" Ziel ") Bild angezeigt wird. Mögliche Werte sind source-over, source-atop, source-in, source-out, destination-over, destination-atop, destination-in, destination-out, xorund copy. Standard ist source-over. Unbekannte Werte werden ignoriert.

Angenommen, wir zeichnen zuerst ein orangefarbenes Quadrat und dann einen cyanfarbenen Kreis. In dieser zusammengesetzten Operation bezeichnen wir das bereits vorhandene Quadrat als Ziel , und der neue Kreis wird als Quelle der Komposition bezeichnet.

Wenn der Kreis mit dem Quadrat übereinstimmt, wird er standardmäßig über das Quadrat gelegt, dh als " Quelle über Ziel" oder " Quelle über Ziel" angezeigt "source-over". Wir können dieses Verhalten aber auch ändern und globalCompositeOperationsagen "destination-over": In diesem Fall wird der Kreis unter dem Quadrat angezeigt, dh das Zielquadrat befindet sich über dem Quellkreis.

Die vorherige Zeichenfläche wurde mit diesem Code generiert:

<canvas id="GlobalCompositeSample" width=90 height=90 style="border: solid 1pt green"> </canvas>
<script>
  var context = document.getElementById('GlobalCompositeSample').getContext('2d');
  // 1. add the orange square
  context.fillStyle = 'orange';
  context.fillRect (0, 0, 60, 60);
  // 2. set the globalCompositeOperation
  context.globalCompositeOperation = "source-over";
  // 3. add the cyan circle
  context.fillStyle = 'cyan';
  context.arc(54,54,36,0,2*Math.PI,false);
  context.fill();
</script>

und wir erzeugen die Beispiele in der folgenden Tabelle im Wesentlichen durch Ersetzen "source-over"durch den entsprechenden alternativen Wert.

"source-over" (Standard)

Zeigt das Quellbild über dem Zielbild an.
"source-atop"

Zeigt die Quelle über dem Zielbild an. Der Teil des Quellbildes außerhalb des Zielbildes wird nicht angezeigt.
"source-in"

Zeigt die Quelle im Zielbild an, dh nur der Teil der Quelle innerhalb des Ziels wird angezeigt und das Ziel ist transparent.
"source-out"

Zeigt nur den Teil des Quellbilds an, der sich außerhalb des Ziels befindet und transparent gemacht wird.
"destination-over"

Zeigt das Ziel über dem Quellbild an.
"destination-atop"

Zeigt das Ziel über dem Quellbild an. Der Teil des Zielbilds, der sich außerhalb der Quelle befindet, wird nicht angezeigt.
"destination-in"

Zeigt nur den Teil des Ziels an, der sich im Quellbild befindet und transparent gemacht wird.
"destination-out"

Zeigt nur den Teil des Ziels an, der sich außerhalb des Quellbilds befindet und transparent gemacht wird.
"lighter"

Zeigt die Quelle zusammen mit dem Zielbild an, der überlappende Bereich wird heller.
"copy"

Ignoriert das Ziel und zeigt nur das Quellbild an.
"xor"

Es werden nur die Bereiche angezeigt, die ausschließlich entweder zum Ziel oder zur Quelle gehören. Überlappende Teile werden ignoriert.

Im Gegensatz zum lighterWert werden auch einige frühere Browser unterstützt darker. Dieser Wert ist jedoch nicht Teil der offiziellen Canvas-Spezifikation.

2.2.5 Farben und Stile

strokeStyleund fillStylesind Kontexteigenschaften, die den Stilwert für den anschließend gezeichneten Strich bzw. die gefüllten Objekte enthalten.

Es gibt drei Arten möglicher Stilwerte: CSS-Farben , ( lineare oder radiale ) Verläufe und Muster .

  1. Eine CSS-Farbe (siehe CSS-Farben unten) ist z. B. einer der Farbnamen (z. B. 'Grün' oder 'Aqua') oder eine hexadezimale Farbe (z '#00FF00'. B. ). Wenn die folgenden gefüllten Objekte grün sein sollen, setzen wir die Zeichenflächeneigenschaft mit say

    context.fillStyle = '#008000';

    oder alternativ und mit demselben Effekt einen Farbnamen anstelle des hexadezimalen Farbwerts verwenden

    context.fillStyle = 'green';
  2. Ein Farbverlauf kann erstellt werden, um Farben zu definieren, die nicht konstant sind, sich jedoch im Bereich allmählich ändern. Wenn wir den Stil auf diese Weise festlegen möchten, müssen wir zuerst ein Objekt erstellen, das die undurchsichtige CanvasGradientSchnittstelle implementiert , mit der wir die addColorStop()Methode aufrufen können . In unserem Skript müssen wir daher tun

    var myNewGradient = context.createLinearGradient (...);   // 1. create a linear gradient
    myNewGradient.addColorStop (...);                         // 2. set a color in the gradient vector field 
    myNewGradient.addColorStop (...);                         //    add yet another color; as many as you like
    context.fillStyle = myNewGradient;                        // 3. finally, let the gradient be new style from now on

    Die gleiche Methode gilt für radiale anstelle von linearen Verläufen und für Striche anstelle von Füllstilen. Die Details sollten in den nachfolgenden Erläuterungen zu linearen und radialen Verläufen deutlich werden.

  3. Ein Muster nimmt ein bestimmtes Bild auf und wiederholt es auf der gesamten Leinwand. Gezeichnete Objekte zeigen dann dieses Muster anstelle einer monochromen Farbe. Die entsprechende Methode, um dies zu erreichen, ist

    createPattern (image, repetition)

2.2.5.1 strokeStyle

CanvasRenderingContext2D.strokeStyle

Enthält den Stilwert für Strichobjekte, bei dem es sich entweder um eine CSS-Farbe, einen Verlauf oder ein Muster handelt (wie in der Einführung erläutert ). Der Standardstilwert ist 'black'.

Im folgenden Beispiel setzen wir zuerst den strokeStyleWert für die grüne Farbe.

<canvas id="StrokeStyleSample1" width=320 height=120> </canvas>
<script>
  var context = document.getElementById('StrokeStyleSample1').getContext('2d');
  // 1. set strokeStyle to a hexadecimal color value, namely '#008000' (same as 'green')
  context.strokeStyle = '#008000'; 
  // 2. draw a kind of half cirlce on the left            
  context.beginPath();
  context.moveTo (60, 10);
  context.lineTo (60, 110);
  context.bezierCurveTo (0, 110, 0, 10, 60, 10);
  context.stroke();
  context.closePath();
  // 3. draw a rectangle on the right top
  context.strokeRect (80, 10, 230, 30);
  // 4. set the text font and write 'Hello!' on the right bottom of the canvas
  context.font = '60pt sans-serif';
  context.strokeText ('Hello!', 80, 110);  
</script>

Nachdem der strokeStyleWert eingestellt ist, werden alle Strichobjekten (gezeichnet mit stroke(), strokeRect()und strokeText()) sind entsprechend gefärbt. Das vorherige Code-Snippet wird folgendermaßen dargestellt:

2.2.5.2 fillStyle

CanvasRenderingContext2D.fillStyle

Enthält den Stilwert für gefüllte Objekte. Wiederum (wie in der Einleitung erläutert ) sind mögliche Werte CSS-Farben, ein Farbverlauf oder ein Muster. Der Standardwert ist 'black'.

Wie im vorherigen Beispiel setzen wir den fillStyleWert erneut auf einen grünen Farbwert.

<canvas id="FillStyleSample1" width=320 height=120> </canvas>
<script>
  var context = document.getElementById('FillStyleSample1').getContext('2d');
  // 1. set strokeStyle to a hexadecimal color value, namely '#008000' (same as 'green')
  context.fillStyle = '#008000'; 
  // 2. draw a kind of half cirlce on the left            
  context.beginPath();
  context.moveTo (60, 10);
  context.lineTo (60, 110);
  context.bezierCurveTo (0, 110, 0, 10, 60, 10);
  context.fill();
  context.closePath();
  // 3. draw a rectangle on the right top
  context.fillRect (80, 10, 230, 30);
  // 4. set the text font and write 'Hello!' on the right bottom of the canvas
  context.font = '60pt sans-serif';
  context.fillText ('Hello!', 80, 110);  
</script>

Die gefüllten Objekte (gezeichnet in Schritt 2-4 mit fill(), fillRect()und fillText()) erscheinen nun grün:

2.2.5.3 createLinearGradient (x0, y0, x1, y1) undaddColorStop (pos, color)

CanvasRenderingContext2D.createLinearGradient (x0, y0, x1, y1)

Erstellt ein CanvasGradientObjekt, das einen linearen Verlauf definiert, der vom Startpunkt (x0,y0)zum Endpunkt verläuft (x1,y1).

CanvasGradient.addColorStop (position, color)

weist dann ein coloran der gegebenen zu position. Dies positionist ein Wert zwischen 0.0(Startpunkt des linearen Gradienten) und 1.0(Endpunkt). Das colorist eine Zeichenfolge, die eine CSS-Farbe darstellt .

Das CanvasGradient(mit hinzugefügten Farben) kann dann als Stilwert für strokeStyleoder definiert werden fillStyle.

Beispiel

Ein typisches Beispiel für einen "Farbverlauf" -Farbstil ist ein Regenbogen, der seine Farbe allmählich ändert. Lassen Sie uns also einen linearen Verlauf erstellen, ihn benennen rainbowGradient, einige Farben hinzufügen, diesen zuweisen fillStyleund dann einen fillRect()über die Größe der gesamten Leinwand zeichnen . Das resultierende Bild ist das folgende:

und das ursprüngliche Code-Snippet zum Erzeugen dieses Bildes ist gegeben durch

<canvas id="Rainbow1" width=600 height=100> </canvas>
<script>
  var context = document.getElementById('Rainbow1').getContext('2d');
  // 1. create the linear gradient
  var rainbowGradient = context.createLinearGradient (100, 50, 500, 50);
  // 2. add colors to the gradient
  rainbowGradient.addColorStop (0,    'red');
  rainbowGradient.addColorStop (0.25, 'yellow');
  rainbowGradient.addColorStop (0.5,  'green');
  rainbowGradient.addColorStop (0.75, 'blue');
  rainbowGradient.addColorStop (1,    'violet');
  // 3. set the fillStyle to this new gradient
  context.fillStyle = rainbowGradient;
  // 4. now draw some filled objects; in this case just a rectangle
  context.fillRect (0, 0, 600, 100);
</script>
Erläuterung des vorherigen Beispiels

Der gesamte Vorgang ist komplizierter als nur das Zuweisen einer Farbe (z. B. 'green') fillStyle. Stattdessen führen wir die folgenden Schritte aus:

  1. Durch einen Anruf

      var rainbowGradient = context.createLinearGradient (100, 50, 500, 50);

    Wir erstellen einen linearen Gradienten (benannt rainbowGradient) vom Startpunkt (100,50) bis zum Endpunkt (500,50).

    Dies wird durch die Linie in der Mitte der Leinwand angezeigt.

  2. Im nächsten Schritt fügen wir dem Farbverlauf fünf Farben hinzu, indem wir aufrufen

    rainbowGradient.addColorStop (position, color);

    fünf Mal.

    Jedes Mal positionist ein Wert zwischen 0und 1der relative Abstand zwischen Start- und Endpunkt. Anstelle von Farbnamen 'red', 'yellow'usw. wir alle genommen haben könnte CSS - Farb String.

  3. Im dritten Schritt weisen wir diesen neu erstellten linearen Verlauf als Wert zu fillStyle

    context.fillStyle = rainbowGradient;

    und wenn wir dann gefüllte Objekte zeichnen, werden diese Objekte gemäß der rainbowGradientSpezifikation gefärbt .

  4. In unserem Beispiel zeichnen wir nur eine fillRect()Übergröße der gesamten Leinwand, wodurch die gesamte Verlaufseinstellung sichtbar wird.

Beispiel mit linearem Verlauf als Strichstilwert

Der ganze Prozess funktioniert auch für mehrere Objekte und für strokeStylestatt fillStyle. Schauen Sie sich diese Leinwand an, um dies zu demonstrieren

generiert durch diesen Code (Schritte 2.-4. sind identisch mit dem strokeStyleBeispielcode ):

<canvas id="RainbowStrokeStyleSample1" width=320 height=120> </canvas>
<script>
  var context = document.getElementById('RainbowStrokeStyleSample1').getContext('2d');
  // 1. set strokeStyle to a linear gradient value
  var rainbowGradient = context.createLinearGradient (0, 60, 320, 60);
  rainbowGradient.addColorStop (0,    'red');
  rainbowGradient.addColorStop (0.25, 'yellow');
  rainbowGradient.addColorStop (0.5,  'green');
  rainbowGradient.addColorStop (0.75, 'blue');
  rainbowGradient.addColorStop (1,    'violet');
  context.strokeStyle = rainbowGradient;
  // 2. draw a kind of half cirlce on the left            
  context.beginPath();
  context.moveTo (60, 10);
  context.lineTo (60, 110);
  context.bezierCurveTo (0, 110, 0, 10, 60, 10);
  context.stroke();
  context.closePath();
  // 3. draw a rectangle on the right top
  context.strokeRect (80, 10, 230, 30);
  // 4. set the text font and write 'Hello!' on the right bottom of the canvas
  context.font = '60pt sans-serif';
  context.strokeText ('Hello!', 80, 110);  
</script>

2.2.5.4 createRadialGradient (x0, y0, r0, x1, y1, r1) undaddColorStop (pos, color)

CanvasRenderingContext2D.createRadialGradient (x0, y0, r0, x1, y1, r1)

erzeugt ein CanvasGradientObjekt , das einen radialen Gradienten läuft von einem Ausgangskreis festlegt (x0,y0,r0)bis zum Ende Kreis (x1,y1,r1). In jedem der beiden Kreise (x,y,r), der Mittelpunkt ist , (x,y)und der Radius ist r, wo x, yund rist (floating point) Zahlen.

CanvasGradient.addColorStop (position, color)

weist dann ein coloran der gegebenen zu position. Dies positionist ein Wert zwischen 0.0(Startpunkt des linearen Gradienten) und 1.0(Endpunkt). Das colorist eine Zeichenfolge, die eine CSS-Farbe darstellt .

Das neu generierte CanvasGradientkann dann als Stilwert für strokeStyleoder definiert werden fillStyle.

Der Aufbau des radialen Gradienten ähnelt dem linearen Gradienten, außer dass das Gradientenfeld von einem Startpunkt (x0,y0)zu einem Endpunkt nicht linear ist (x1,y1). Stattdessen ist sein Ursprung ein Kreis (x0,y0,r0), der sich in Richtung eines Endkreises bewegt (x1,y1,r1).

Beispiel

Mit einem radialen Farbverlauf können wir 3D-Effekte erzeugen, z. B. eine monochrome rote Scheibe in eine Kugel verwandeln, die von einer Lichtquelle beleuchtet wird:

Dieses Bild wird mit dem folgenden Code erstellt

<canvas id="RadialSample" width=240 height=240> </canvas>
<script>
  var context = document.getElementById('RadialSample').getContext('2d');
  // 1. create a radial gradient
  var rg = context.createRadialGradient (80, 80, 20, 120, 120, 110);
  // 2. add colors
  rg.addColorStop (0, 'yellow');
  rg.addColorStop (1, 'red');
  // 3. set the fill style to the new gradient
  context.fillStyle = rg;
  // 4. now draw some filled objects; in this case just a circle
  context.beginPath();
  context.arc (120,120,110,0,2*Math.PI,false);
  context.fill();
  context.closePath();
</script>

Die Schritte sind wie folgt:

  1. Wir beginnen mit der Erzeugung eines radialen Gradienten, den wir nennen rg

    var rg = context.createRadialGradient (80, 80, 20, 120, 120, 110);

    Die Argumente dieses Aufrufs sind die Parameter für zwei Partikel: Der Startkreis (x0,y0,r0)= (80,80,20)mit Mittelpunkt (80,80)und Radius 20und der Endkreis (x1,y1,r1)= (120,120,110).

    Der Prozess vom allmählichen Wechsel vom Startkreis zum Endkreis kann wieder zu einem kontinuierlichen Prozess von Position 0.0zu Position nummeriert werden 1.0.
    Zum Beispiel sind die fünf Positionen 0.0, 0.25, 0.5, 0.75und 1.0sind

  2. Wir fügen dem Radienten nun Farbstopps hinzu. In unserem Fall nur 'yellow'an der Startposition 0.0und 'red'an der Endposition1.0

    rg.addColorStop (0, 'yellow');
    rg.addColorStop (1, 'red');
  3. Der Verlauf rgwird nun als Stilwert zugewiesenfillStyle

    context.fillStyle = rg;

    so dass alle nachfolgend gezeichneten gefüllten Objekte diese "Farbe" annehmen rg.

  4. Wir zeichnen schließlich gefüllte Objekte, in diesem Beispiel nur einen gefüllten Kreis, der genau den Endkreis des radialen Gradienten abdeckt

    context.beginPath();
    context.arc (120,120,110,0,2*Math.PI,false);
    context.fill();
    context.closePath();
Variationen des vorherigen Beispiels

Wenn wir anstelle des Kreises ein gefülltes Rechteck über die Größe der gesamten Leinwand gezeichnet hätten, dh wenn diese eine Linie

  context.fillRect (0, 0, 240, 240);

würde den Code im vorherigen Schritt 4 ersetzen, dann ist das Bild dies

Wenn wir dann die Farben 'yellow'nach 'white'und 'red'nach ersetzen 'black', erhalten wir

Und schließlich, wenn wir nicht nur zwei Farbstopps hinzufügen, sondern stattdessen diese fünf

  rg.addColorStop (0, 'red');
  rg.addColorStop (0.25, 'yellow');
  rg.addColorStop (0.5, 'green');
  rg.addColorStop (0.75, 'blue');
  rg.addColorStop (1, 'violet');

das Bild wird

Variationen des radialen Gradienten

Es ist wahrscheinlich wichtig, viele Beispiele zu üben, um mit den Verläufen umzugehen. Das folgende Beispiel zeigt Variationen des Start- und Endkreises des radialen Gradienten. Jedes Mal verwenden wir diese Codevorlage:

<canvas id="RadialGradient1" width=200 height=200> </canvas>
<script>
  var context = document.getElementById('RadialGradient1').getContext('2d');
  context.addGrid();
  var rainbowGradient = context.createRadialGradient (x0, y0, r0, x1, y1, r1);
  rainbowGradient.addColorStop (0, 'orange');
  rainbowGradient.addColorStop (1, 'cyan');
  context.fillStyle = rainbowGradient;
  context.fillRect (0, 0, 200, 200);
</script>

und in jedem konkreten Beispiel werden die sechs Parameter in context.createRadialGradient (x0, y0, r0, x1, y1, r1)variiert. Das erste Bild in jeder Spalte zeigt, wie die Leinwand gerendert wird. Das zweite Bild zeigt die Position des orangefarbenen Start- und des Cyan-Endkreises.

(x0,y0,r0,x1,y1,r1)=(100,100,10,100,100,75) (x0,y0,r0,x1,y1,r1)=(80,80,10,120,120,75) (x0,y0,r0,x1,y1,r1)=(50,50,50,150,150,50) (x0,y0,r0,x1,y1,r1)=(30,30,10,150,150,50)

2.2.5.5 createPattern (image, repetition)

CanvasRenderingContext2D.createPattern (image, repetition)

Gibt ein CanvasPatternObjekt zurück, das als Wert für eine Stileigenschaft verwendet werden kann (siehe fillStyleund strokeStyle).
Das imageArgument muss a HTMLImageElement, a HTMLVideoElementoder ein anderes sein HTMLCanvasElement.
Das repetitionArgument muss einer der folgenden Werte sein: 'repeat-x'(für die horizontale Wiederholung des Bildes), 'repeat-y'(für die vertikale Wiederholung des Bildes), 'repeat'(sowohl für die horizontale als auch für die vertikale Wiederholung) oder 'no-repeat'(für keine der beiden Wiederholungen). Der Standardwert ist 'repeat'.

Tatsächlich ist in Firefox überhaupt 'repeat'der einzige akzeptierte Wert für repetition.

Beispiel

Verwenden wir beispielsweise die JPG-Datei mit einer Größe von 49 × 70 Pixel grayhorse.jpg

als Musterbild für ein CanvasPatternObjekt und verwenden Sie dieses Objekt als Wert für die fillStyleEigenschaft. Anschließend zeichnen wir einige gefüllte Objekte: mit fillRect(), einen fill()Pfad und einige fillText(). Das vollständige Code-Snippet

<canvas id="CreatePatternSample1" width=600 height=210> </canvas>
<script>
  var context = document.getElementById('CreatePatternSample1').getContext('2d');
  // create an Image object from the grayhorse.jpg file
  var horseImage = new Image ();
  horseImage.src = 'grayhorse.jpg';
  // create a CanvasPattern object with this image and make that the new fillStyle value
  var horsePattern = context.createPattern (horseImage, 'repeat');
  context.fillStyle = horsePattern;
  // Now draw same objects: first a rectangle
  context.fillRect (0, 0, 200, 210);
  // then a triangle
  context.beginPath();
  context.moveTo (220, 0);
  context.lineTo (220, 100);
  context.lineTo (550, 50);
  context.lineTo (220, 0);
  context.fill();
  context.closePath();
  // and finally write "Hello!" 
  context.font = '120px sans-serif';
  context.fillText ('Hello!', 210, 200);
</script>

rendert in deinem Browser so

2.2.6 Linienkappen und Verbindungen

Neben den allgemeinen Stil - Einstellungen gibt es spezielle Stileigenschaften definieren , die Darstellung von Linien, ihre Kappen und schließt sich, nämlich lineWidth, lineCapund lineJoin. Und um die Spitzen von spitzen Winkeln zu begrenzen, gibt es auch miterLimit.

2.2.6.1 lineWidth

CanvasRenderingContext2D.lineWidth

Enthält eine Gleitkommazahl, die die aktuelle Linienstärke definiert (in Koordinatenraumeinheiten). Der Standardwert ist 1.0.

Die folgende Zeichenfläche zeigt verschiedene lineWidthEinstellungen von 0.1bis 20.0. In jedem Linienbeispiel setzen wir zuerst context.lineWidthdie entsprechende Zahl und zeichnen dann die Linie.

Beachten Sie, dass alle Linien mit der strokeStyleEinstellung (Standard) gezeichnet werden 'black', viele Linien jedoch grauer als schwarz erscheinen und ihre Kanten nicht sehr scharf sind. Dies ist kein Browser-Fehler, sondern hat damit zu tun, dass die Zeichenfläche in Pixel zerlegt wird. Wenn eine schwarze Linie oder Form ein weißes Pixel nur teilweise bedeckt, ist die tatsächliche Farbe dieses Pixels ein mittlerer Grauwert.

Im folgenden vergrößerten Canvas-Beispiel umfasst eine Linie von (0, 2) bis (10, 2) 20 halbe Pixel. Diese betroffenen Pixel werden daher grau dargestellt. Die Linie von (0, 1,5) bis (10, 1,5) passt dagegen vollständig in die betroffenen zehn Pixel, die Linie wird gestochen scharf und schwarz gerendert.

2.2.6.2 lineCap

CanvasRenderingContext2D.lineCap

definiert den Stil der Zeilenenden. Mögliche Werte sind 'butt', 'round'oder 'square'. Der Standardwert ist 'butt'.

Im folgenden Beispiel zeichnen wir dreimal drei Linien von der oberen zur unteren orangefarbenen Linie. Das lineWidthist auf 5.0, 10.0und 20.0, respectively. Das lineCapist auf 'butt', 'round'und 'square', wie in der Abbildung erläutert. Beachten Sie, dass die Linien mit den Zeilen roundund den squareKappen ihre Endpunkte um die Hälfte der Linienbreite überschreiten.

2.2.6.3 lineJoin

CanvasRenderingContext2D.lineJoin

definiert den Linienstil an ihrem Treffpunkt. Mögliche Werte sind 'round', 'bevel'oder 'miter'. Der Standardwert ist 'miter'.

In den folgenden drei Canvas-Beispielen wird jeweils dieselbe lineJoinLinienfolge gezeichnet , mit der Ausnahme, dass die Eigenschaft jedes Mal auf den entsprechenden Wert gesetzt wird, bevor die Linien gezeichnet werden.

2.2.6.4 miterLimit

CanvasRenderingContext2D.miterLimit

Die Gehrungslänge ist der Abstand zwischen dem Punkt, an dem die Verbindung zweier Linien erfolgt, und dem Schnittpunkt der Linienkanten an der Außenseite der Verbindung. Das mit dem Standardwert festgelegte Gehrungsgrenzverhältnis ist das maximal zulässige Verhältnis der Gehrungslänge zur halben Linienbreite. Wenn die Gehrungslänge dazu führen würde, dass das Gehrungsgrenzverhältnis überschritten wird, wird die Ecke so angezeigt, als wäre sie auf eingestellt .miterLimit10.0lineJoinbevel

2.2.7 Schatten

Das CanvasRenderingContext2Dkommt mit netten Werkzeugen, um allen Objekten auf der Leinwand ausgefallene Schatten hinzuzufügen, sogar Text. Es gibt vier Eigenschaften, die das Vorhandensein und das Aussehen des Schattens bestimmen: shadowOffsetX, shadowOffsetY, shadowBlurund shadowColor.

Es ist sinnvoll zu glauben, dass alle zum Kontext hinzugefügten Objekte Schatten haben. Außer diese Schatten in der Default - Einstellung sind unsichtbar, weil dann shadowOffsetXund shadowOffsetYsind auf 0, shadowBlurist 0 und das shadowColorist transparent.

Die folgende Beispielleinwand zeigt drei Spalten mit jeweils unterschiedlichen Schatteneinstellungen. Die erste Spalte #1zeigt den Standardfall, #2hat einen scharfen orangefarbenen Schatten und in der Spalte #3sind die Schatten unscharf und halbtransparent schwarz.

Der Quellcode für die Just Show Canvas lautet wie folgt

<canvas id="ShadowSample0" width=600 height=330 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowSample0").getContext("2d");
  // General settings
  context.font = '40pt Arial';               // sets the font
  context.lineWidth = '5'                    // sets the line width for the strokeRect() calls below
  context.strokeStyle = 'green'              // sets the color for the strokeRect() calls below
  context.fillStyle = 'red';                 // sets the color for the fillText() and fillRect() calls below
  // #1 column with default (= no) shadow
  context.fillText ("# 1", 25, 45);
  context.fillRect (25, 70, 100, 100);
  context.strokeRect (25, 200, 100, 100);
  // #2 column  
  context.shadowOffsetX = 15;                // shadow appears 15 pixel to the right
  context.shadowOffsetY = 15;                // shadow appears 15 pixel to the botttom
  context.shadowColor = 'orange';            // shadow color now is 'orange'
  context.shadowBlur = 0;                    // zero blur: this is a crisp shadow
  context.fillText ("# 2", 225, 45);
  context.fillRect (225, 70, 100, 100);
  context.strokeRect (225, 200, 100, 100);
  // #3 column  
  context.shadowOffsetX = 15;                // again, lets shadows appear 15 pixel to the right
  context.shadowOffsetY = 15;                // ... and 15 pixel to the bottom
  context.shadowColor = 'rgba(0,0,0,0.5)';   // shadow color is black, but half transparent
  context.shadowBlur = 5;                    // shadow is blurred
  context.fillText ("# 3", 425, 45);
  context.fillRect (425, 70, 100, 100);
  context.strokeRect (425, 200, 100, 100);  
</script>

2.2.7.1 shadowOffsetX

CanvasRenderingContext2D.shadowOffsetX

enthält eine Gleitkommazahl, um den Schattenversatz in horizontaler Richtung zu definieren. Standardmäßig ist es auf eingestellt 0.

Hier ist ein Beispiel mit vier verschiedenen shadowOffsetYWerten:

In #1der shadowOffsetXStandardeinstellung ist 0: Es gibt keinen Schatten, da er sich direkt hinter den Textzeichen befindet. In #2der Horizontalen wird der Versatz auf gesetzt, 10bevor der Text geschrieben wird, sodass er zehn Pixel rechts hinter dem Text angezeigt wird. In #3und #4das shadowOffsetXist -15und 75jeweils. Der Quellcode für all dies ist

<canvas id="ShadowOffsetXSample" width=650 height=60 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowOffsetXSample").getContext('2d');
  // settings for the use of fillText below
  context.fillStyle = 'black';
  context.font = '40pt Arial';
  // set the shadow color to a visible 'orange'
  context.shadowColor = 'orange';
  // #1 text with horizontal shadow offset 0 (i.e. shadow behind the text and thus invisible)
  context.shadowOffsetX = 0;
  context.fillText ("#1",  50, 50);
  // #2 text with horizontal shadow offset 10 (i.e. 10 pixel to the right)
  context.shadowOffsetX = 10;
  context.fillText ("#2", 200, 50);
  // #3 text with horizontal shadow offset -10 (i.e. 10 pixel to the left)
  context.shadowOffsetX = -10;
  context.fillText ("#3", 350, 50);
  // #4 text with horizontal shadow offset 75 (i.e. 75 pixel to the right)
  context.shadowOffsetX = 75;
  context.fillText ("#4", 500, 50);
</script>

2.2.7.2 shadowOffsetY

CanvasRenderingContext2D.shadowOffsetY

enthält eine Gleitkommazahl, um den Schattenversatz in vertikaler Richtung zu definieren. Standardmäßig ist es auf eingestellt 0.

Hier ist ein Beispiel mit vier verschiedenen shadowOffsetYWerten, auf Standard eingestellt 0, 10, -10und 75jeweils:

Der Quellcode für das vorherige Beispiel lautet:

<canvas id="ShadowOffsetYSample" width=650 height=130 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowOffsetYSample").getContext('2d');
  // settings for the use of fillText below
  context.fillStyle = 'black';
  context.font = '40pt Arial';
  // set the shadow color to a visible 'orange'
  context.shadowColor = 'orange';
  // #1 text with vertical shadow offset 0 (i.e. shadow behind the text and thus invisible)
  context.shadowOffsetY = 0;
  context.fillText ("#1",  50, 50);
  // #2 text with vertical shadow offset 10 (i.e. 10 pixel down)
  context.shadowOffsetY = 10;
  context.fillText ("#2", 200, 50);
  // #3 text with vertical shadow offset -10 (i.e. 10 pixel up)
  context.shadowOffsetY = -10;
  context.fillText ("#3", 350, 50);
  // #4 text with vertical shadow offset 75 (i.e. 75 pixel down)
  context.shadowOffsetY = 75;
  context.fillText ("#4", 500, 50);
</script>

2.2.7.3 shadowBlur

CanvasRenderingContext2D.shadowBlur

Enthält eine Gleitkommazahl, um den aktuellen Grad der Unschärfe zu definieren, der auf Schatten angewendet wird. Standardmäßig ist es auf eingestellt 0.

Das folgende Beispiel zeigt der Schatten für vier verschiedene Unschärfe Ebene, nämlich den Standard 0, 2, 4und 8jeweils:

Der Quellcode für das vorherige Beispiel lautet:

<canvas id="ShadowBlurSample1" width=650 height=70 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowBlurSample1").getContext('2d');
  // settings for the use of fillText below
  context.fillStyle = 'black';
  context.font = '40pt Arial';
  // set the shadow color to a visible 'orange' and the x- and y-offset to 15
  context.shadowColor = 'orange';
  context.shadowOffsetX = 15;
  context.shadowOffsetY = 15;
  // #1 
  context.shadowBlur = 0;
  context.fillText ("#1",  50, 50);
  // #2 
  context.shadowBlur = 2;
  context.fillText ("#2", 200, 50);
  // #3
  context.shadowBlur = 4;
  context.fillText ("#3", 350, 50);
  // #4 
  context.shadowBlur = 8;
  context.fillText ("#4", 500, 50);
</script>

Selbst wenn shadowOffsetXund shadowOffsetYbeide 0so sind, dass sich der Schatten direkt hinter den gezeichneten Objekten befindet, erzeugt er dennoch einen sichtbaren Effekt, wenn er beispielsweise wie im folgenden Beispiel shadowBlureinen positiven Wert hat5

Der Quellcode für das vorherige Beispiel:

<canvas id="ShadowBlurSample2" width=750 height=75 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowBlurSample2").getContext('2d');
  // text settings
  context.font = '28pt Arial';
  context.fillStyle = 'red';
  // shadow settings
  context.shadowColor = 'blue';
  context.shadowBlur = 5;
  context.shadowOffsetX = 0;
  context.shadowOffsetY = 0;
  // add the text
  context.fillText ("shadow: blur 5, color blue, x- and y-offset 0", 20, 50);
</script>

2.2.7.4 shadowColor

CanvasRenderingContext2D.shadowColor

enthält den CSS-Farbwert (siehe CSS-Farben ) für die aktuelle Schattenfarbe. Standardmäßig ist es auf eingestellt transparent black.

Es werden vier verschiedene Einstellungen für shadowColorangezeigt

und der Quellcode für das vorherige Canvas-Beispiel ist dieser

<canvas id="ShadowColorSample" width=650 height=70 style="border: solid 1pt blue;"> </canvas>
<script>
  var context = document.getElementById("ShadowColorSample").getContext('2d');
  // settings for the use of fillText below
  context.fillStyle = 'black';
  context.font = '40pt Arial';
  // set the shadow color to a visible 'orange' and the x- and y-offset to 15
  context.shadowBlur = 3;
  context.shadowOffsetX = 15;
  context.shadowOffsetY = 15;
  // #1
  context.shadowColor = 'aqua';
  context.fillText ("#1",  50, 50);
  // #2 
  context.shadowColor = 'rgba(0%,0%,0%,0.5)';
  context.fillText ("#2", 200, 50);
  // #3
  context.shadowColor = '#CCCCCC';
  context.fillText ("#3", 350, 50);
  // #4
  context.shadowColor = 'hsl(120,50%,50%)';
  context.fillText ("#4", 500, 50);
</script>

2.2.8 Einfache Formen (Rechtecke)

Der 2D-Kontext kennt nur eine Art einfacher Formen, "einfach" in dem Sinne, dass er mit einem einzigen Methodenaufruf gezeichnet werden kann, nämlich Rechtecke . Natürlich können alle Arten von Formen gezeichnet werden: Kreise, Dreiecke, komplexe Polygone , glatte Bézier-Kurven . All dies muss jedoch durch den Bau komplexerer Pfade zusammengesetzt werden .

Es gibt folgende Methoden für Rechtecken: fillRect(), strokeRect()und clearRect(). Es gibt auch die einfache rect()Methode, die jedoch nur als Teil einer Pfaddefinition dient und daher später im Abschnitt über komplexe Formen erläutert wird .

2.2.8.1 fillRect (x, y, w, h)

CanvasRenderingContext2D.fillRect (x, y, w, h)

malt ein festes oder "gefülltes" Rechteck mit den aktuellen Einstellungen für fillStyle. Dieses Rechteck hat seine linke obere Ecke bei (x,y), ist wPixel breit und hPixel hoch. x, y, wUnd hsind (floating point) Zahlen.

Hier ist ein Beispiel einer Leinwand mit drei "gefüllten" Rechtecken

Der Quellcode für das vorherige Bild ist dieser

// 1. draw a filled rectangle in default black
context.fillStyle = 'black';                            // this line is actually superfluous, because 'black' is default
context.fillRect(50,50,100,100);                        // draw the first rectangle

// 2. draw a filled rectangle in redd
context.fillStyle = 'red';                              // change the fillStyle to the color 'red'
context.fillRect(200,50,100,100);                       // draw the second rectangle

// 3. draw a fancy rectangle with linear gradient
var lg = context.createLinearGradient(350,50,450,150);  // create a linear gradient
lg.addColorStop(0,'yellow');                            // start (=0) the gradient with 'yellow'
lg.addColorStop(1,'red');                               // finish (=1) the gradient with 'red'
context.fillStyle = lg;                                 // set the fillStyle to this new linear gradient
context.fillRect(350,50,100,100);                       // draw the third rectangle

In jedem der drei Fälle setzen wir zuerst das fillStyleund rufen dann fillRect()auf, um ein entsprechendes Rechteck zu zeichnen. Standardmäßig fillStyleist die Farbe eingestellt 'black'. Im zweiten Beispiel setzen wir es auf 'red'und im dritten Beispiel ist es keine einfache Farbe, sondern ein linearer Farbverlauf .

2.2.8.2 strokeRect (x, y, w, h)

CanvasRenderingContext2D.strokeRect (x, y, w, h)

ein Rechteck auf die Leinwand zieht, die an ihrer linken oberen Ecke hat (x,y), ist wPixel breit und hPixel hoch. Der tatsächliche Stil der Zeichnung wird durch die Einstellungen in strokeStyleund die aktuellen Linien- und Schatteneigenschaften bestimmt .

Ähnlich wie im vorherigen Beispielbereich mit gefüllten Rechtecken erstellen wir jetzt einen ähnlichen Bereich, indem wir alle fillStyleVorkommen durch a strokeStyleund alle fillRect()durch strokeRect()Aufrufe ersetzen . Darüber hinaus demonstrieren wir die Variation des lineWidth, da dies auch das Outfit des endgültigen Bildes bestimmt.

wird durch diesen Code generiert:

// 1. draw a rectangle with black strokes 
context.lineWidth = 1.0;                                // this is actually superfluous, because 1.0 is default
context.strokeStyle = 'black';                          // this is superfluous, too, because 'black' is default anyway 
context.strokeRect(50,50,100,100);                      // draw the first rectangle

// 2. draw a rectangle with red strokes
context.lineWidth = 7.0;                                // change the line width to 7.0
context.strokeStyle = 'red';                            // change the strokeStyle to the color 'red'
context.strokeRect(200,50,100,100);                     // draw the second rectangle

// 3. draw a rectangle with strokes in gradient style
context.lineWidth = 12.0;                               // increase the line width, once more
var lg = context.createLinearGradient(350,50,450,150);  // define a linear gradient
lg.addColorStop(0,'yellow');                            // start the gradient with the color 'yellow'
lg.addColorStop(1,'red');                               // let the gradient end with the color 'red'
context.strokeStyle = lg;                               // set the strokeStyle to this new gradient
context.strokeRect(350,50,100,100);                     // draw the third rectangle

2.2.8.3 clearRect (x, y, w, h)

CanvasRenderingContext2D.clearRect (x, y, w, h)

Löscht den Rechteckbereich, der durch die linke obere Ecke (x,y), Breite wund Höhe definiert ist h.

Stellen Sie sich clearRect()einen Radiergummi vor, der alle vorherigen Zeichnungen darin löscht.

Beispiel

Die folgende Leinwand wird zuerst mit einer grünen Farbe gefüllt (mittels eines fillRect()Aufrufs). Dann wird ein gelbes rechteckiges Feld hinzugefügt. Und schließlich clearRect()löscht ein Anruf wieder einen großen Teil des Bildes (nämlich alles rechts und unten von (100,50)).

Der Quellcode für die vorherige Zeichenfläche lautet wie folgt:

// 1. fill the whole canvas area with a transparent green color
context.fillStyle = 'rgba(0%,100%,0%,0.3)';  // green color with 0.3 transparency
context.fillRect (0,0,300,200);              // filled rectangle over the whole canvas area

// 2. draw a yellow rectangular box 
context.lineWidth = 10.0;                    // set the line width to 10
context.strokeStyle = 'yellow';              // set the strokeStyle to the color 'yellow'
context.strokeRect(25,25,100,100);           // draw the yellow box

// 3. cut out a rectangle from the canvas  
context.clearRect (100,50,200,150);          // delete all content in this rectangle  

2.2.9 Komplexe Formen (Pfade)

Mit strokeRect()und können fillRect()wir (Striche oder gefüllte) Rechtecke zeichnen. In den meisten Fällen möchten wir aber auch andere und komplexere Formen zeichnen. Dies kann schrittweise durch Erstellen von Pfaden erfolgen . Das Standardverfahren zum Erstellen einer komplexen Form ist daher

  1. Beginnen Sie einen neuen Pfad mit beginPath().

  2. Erstellen Sie eine Folge von primitiven Schritten, wobei jeder Schritt vom aktuellen Punkt im Pfad zu einem neuen wechselt. Mögliche primitive Schritte sind: (mit zu einem neuen Punkt zu bewegen moveTo()), um eine gerade Linie zu einem neuen Punkt erstellen (mit lineTo()), einem rechteckigen Kasten (mit rect()) oder einer irgendwie gekrümmten Linie (mit quadraticCurveTo(), bezierCurveTo(), arc()oder arcTo().

  3. Schließen Sie optional den Pfad mit closePath(), dh ziehen Sie erneut eine Linie vom aktuellen Endpunkt zum Anfangspunkt. Und dann tatsächlich den Weg mit entweder stroke()oder zeichnen fill().

Als nächstes zeigen wir, wie wir die Methoden zum Zeichnen von Rechtecken durch Erstellen von Pfaden reproduzieren können. Anschließend geben wir einen Überblick über diese Bausteine ??für Pfade mit vielen Beispielen.

Leider ist es notwendig, das Pfadkonzept anhand vieler Beispiele zu erläutern, da es selbst im [W3C] -Standard kein gut ausgearbeitetes Konzept ist . Im vorhergehenden [WHATWG] -Standard wurden PathObjekte explizit definiert und das Konzept war viel umfassender. In [W3C] wurden jedoch viele dieser Konzepte eliminiert. Pfade werden jetzt intern hinter den Szenen behandelt, und wir haben nur noch Möglichkeiten, auf das CanvasRenderingContext2DObjekt ( beginPath()usw.) einzuwirken . Dies reduzierte die Größe der API, hilft jedoch nicht wirklich, das Pfadkonzept zu verstehen. Nun, damit müssen wir leben. Und dennoch bleibt die ursprüngliche Ausdruckskraft erhalten: Es ist möglich, über alles zu zeichnen, was wir möglicherweise wollen können.

Beispiel: Neu definieren fillRect()durch die Pfadmethode

Wir können ein gefülltes Rechteck mit einem einzigen Aufruf von zeichnen fillRect(). Zum Beispiel dieser Code

context.fillStyle = 'rgba(255,0,0,0.5)';   // fill style is now set to a half transparent red color
context.fillRect (80, 20, 100, 60);        // draw the rectangle with left upper corner (80,20), width 100 and height 60

erzeugt dieses Bild

Anstatt anzurufen fillRect(), hätten wir einen Pfad entlang der Form des Rechteckbereichs und dann danach zeichnen fill()können. Das folgende Codefragment erzeugt somit genau das gleiche Bild wie das vorherige:

// set the style for the fill() call below
context.fillStyle = 'rgba(255,0,0,0.5)';
// now create the path:
context.beginPath();                       // 0. start a new path
context.moveTo (80, 20);                   // 1. moves the current point to (80,20)
context.lineTo (180, 20);                  // 2. horizontal line from (80,20) to (180,20)
context.lineTo (180, 80);                  // 3. vertical line from (180,20) to (180,80)
context.lineTo (80, 80);                   // 4. horizontal line from (180,80) to (80,80)
context.lineTo (80, 20);                   // 5. vertical line from (80,80) back to the start at (80,20)
context.fill();                            // 6. draw the solid figure with the shape of the path lines

In dieser Version zeichnen wir den Pfad zunächst in fünf Schritten:

Beachten Sie, dass bei jedem moveTo()Schritt (Schritt 1., angezeigt durch die Cyan-Linie) der aktuelle Punkt des Pfads nur an die neu angegebene Position verschoben wird. Jeder lineTo()Schritt (Linien in Schwarz) zeichnet tatsächlich eine Linie von der vorherigen Position zum neuen angezeigten Punkt. Nachdem der Pfad umrissen wurde, erstellt ein Aufruf von fill()ein gefülltes Objekt fillStyleinnerhalb der angegebenen Zeilen. In unserem Fall ergibt sich das gefüllte rote Rechteck.

Beispiel: Neu definieren strokeRect()durch die Pfadmethode

Natürlich wird mit dieser Pfadmethode ein rotes "Strichrechteck" gezeichnet, wenn wir das fill()durch die stroke()Methode ersetzen , nachdem wir festgelegt haben, strokeStylewo das fillStylevorher war. Insgesamt das Code-Snippet

// set the style for the stroke() call below
context.strokeStyle = 'rgba(255,0,0,0.5)';          // now: strokeStyle instead of fillStyle
// the same path again as before:
context.beginPath();
context.moveTo (80, 20);
context.lineTo (180, 20);
context.lineTo (180, 80);
context.lineTo (80, 80);
context.lineTo (80, 20);
context.stroke();                                 // now: stroke() insted of fill()
context.closePath();

zeigt dieses Bild

Das ist natürlich das gleiche Bild wie das, das mit der strokeRect()Methode von erzeugt wurde

context.strokeStyle = 'rgba(255,0,0,0.5)';
context.strokeRect (80, 20, 100, 60);
Erstellen eines Rechtecks ??mit rect()

Übrigens, als noch eine Variation desselben Bildes: Wir können auch ein Rechteck mit einem Aufruf von erstellen rect(), so dass

context.strokeRect (x, y, w, h);

ist das gleiche wie

context.beginPath();
context.rect (x, y, w, h);
context.stroke();

Und dementsprechend

context.fillRect (x, y, w, h);

kann zerlegt werden in

context.beginPath();
context.rect (x, y, w, h);
context.fill();

2.2.9.1 beginPath()

CanvasRenderingContext2D.beginPath()

beginnt einen neuen oder setzt den aktuellen Pfad zurück.
Ein neuer Weg wird dann gebaut mit moveTo(), lineTo(), rect(), quadraticCurveTo(), bezierCurveTo(), arc()und arcTo().
Das eigentliche Zeichnen des Pfades erfolgt mit stroke()oder fill().
Ein Aufruf von closePath()schließt zwar den neuen Pfad, jedoch in dem Sinne, dass der aktuelle Punkt mit dem anfänglichen Startpunkt verbunden ist. Der Pfad wird nicht gezeichnet.

Für gängige Anwendungen beginPath()siehe die folgenden Beispiele in den Abschnitten zur Erläuterung stroke()und fill().

2.2.9.2 closePath()

CanvasRenderingContext2D.closePath()

In einem offenen Pfad "schließt" dieser Methodenaufruf den Pfad in dem Sinne, dass er den aktuellen Punkt mit dem Startpunkt des Pfads verbindet. Beachten Sie, dass closePath()der Pfad dadurch nicht in dem Sinne "beendet" wird, in dem er gezeichnet wird. Dazu müssen Sie noch die Methode stroke()oder aufrufen fill().

Zum Beispiel dieses Code - Schnipsel (wo lineWidthfestgelegt ist 5.0und strokeStyleauf 'red')

context.beginPath();
context.moveTo(150,25);      // starting point at the top of the triangle
context.lineTo(250,100);     // line to right bottom corner
context.lineTo(50,100);      // line to left bottom corner
context.stroke();            // draw the lines

erzeugt dieses "unvollständige Dreieck" Bild

Aber wenn wir jetzt ein closePath()vor dem stroke()Anruf einfügen

context.beginPath();
context.moveTo(150,25);      // starting point at the top of the triangle
context.lineTo(250,100);     // line to right bottom corner
context.lineTo(50,100);      // line to left bottom corner
context.closePath();         // closes the shape with a line from the left bottom to the initial top point
context.stroke();            // draw the lines

Die Form ist "geschlossen", das Dreieck ist vollständig: 23

Beachten Sie, dass closePath()das nicht zeichnet

Jeder beginPath()benötigt entweder eine fill()oder stroke()irgendwann später, um tatsächlich eine Figur auf der Leinwand zu erstellen. Nur ein zu setzen, closePath()schließt diesen Weg zwar, zeichnet ihn aber nicht. Das Weglassen des stroke()Aufrufs im vorherigen Beispiel hätte zu einer leeren Leinwand (Bild) geführt.

Beachten Sie, dass ein closePath()Anruf vor einem fill()Anruf überflüssig ist

Der stroke()Befehl des vorherigen Beispiels zeichnet die Form des Pfades, wobei fill()wir die vollständige Form zeichnen. Genauer gesagt, wenn wir ersetzen stroke()durch fill()diesen Code und laufen (jetzt mit fillStyleSet 'red')

context.beginPath();
context.moveTo(150,25);
context.lineTo(250,100);
context.lineTo(50,100);
context.closePath();           // (superfluous, actually)
context.fill();                // draw the full triangle shape

Wir erhalten dieses Bild

Da ein Pfad ohnehin geschlossen werden muss, bevor wir die Form "füllen" können, fill()schließt der Befehl den Pfad standardmäßig. Dies macht den closePath()Anruf überflüssig. Das Weglassen dieser Codezeile hätte genau das gleiche Bild erzeugt.

2.2.9.3 stroke()

CanvasRenderingContext2D.stroke()

Zeichnet den Umriss der durch den aktuellen Pfad definierten Form. Die Stileinstellungen für die eigentliche Zeichnung sind diejenigen, die in strokeStyleden aktuellen Eigenschaften für Linien und Schatten gespeichert sind .

Üblicherweise wird ein Pfad mit einem anfänglichen erstellt beginPath(), gefolgt von einer Reihe von moveTo(), lineTo(), rect(), quadraticCurveTo(), bezierCurveTo(), arc()und arcTo()Anrufen. Ein letzter stroke()Aufruf zeichnet dann die Form.

Beispiel

Die folgende Leinwand enthält zwei Pfade für zwei Formen

Der Quellcode ist dies

// settings for both shapes
context.lineWidth = 10.0;           // set the line width for both stroke figures to 10
// the first shape
context.beginPath();                // start a new path
context.strokeStyle = 'lime';       // set the color for subsequently called stroke() calls to 'lime'
context.moveTo (25, 25);            // go to point (25,25) and make that the current point in the path
context.lineTo (25, 125);           // add a line from (25,25) to (25,125) to the path
context.lineTo (125, 125);          // add another line from (25,125) to 125,125) to the path 
context.stroke();                   // now the path is drawn, according to the lineWith and strokeStyle properties
// the second shape
context.beginPath();                // start another path
context.strokeStyle = 'maroon';     // now set the color for coming stroke drawings to 'maroon'
context.moveTo (175, 25);           // go to (175,25) 
context.lineTo (175, 125);          // add a line from (175,25) to (175,125) to the path
context.lineTo (275, 125);          // add a line from (175,125) to (275,125) to the path
context.stroke();                   // eventually draw the path in stroke style
Geändertes Beispiel mit geschlossenen Pfaden

Wenn wir eine Zeile hinzufügen

context.closePath();

vor jedem der beiden Vorkommen der

context.stroke();

Zeilen im vorherigen Code sind die Pfade geschlossen und werden jeweils zu einem Dreieck. Das Bild ist dann das

2.2.9.4 fill()

CanvasRenderingContext2D.fill()

Zeichnet den gesamten Bereich der durch den aktuellen Pfad definierten Form. Die Stileinstellungen für die eigentliche Zeichnung sind diejenigen, die in gespeichert sind, fillStyleund die aktuellen Eigenschaften für Schatten .

Üblicherweise wird ein Pfad mit einem anfänglichen erstellt beginPath(), gefolgt von einer Reihe von moveTo(), lineTo(), rect(), quadraticCurveTo(), bezierCurveTo(), arc()und arcTo()Anrufen. Ein letzter fill()Aufruf zeichnet dann die Form, die mit der durch definierten Farbe (Farbverlauf oder Muster) gefüllt ist fillStyle.

Beispiel

Die folgende Leinwand besteht aus zwei "gefüllten" Dreiecken

Der Quellcode für dieses Bild ist dieser

// the first triangle
context.fillStyle = 'lime';    // set the color for shapes subsequently drawn with fill() to 'lime'
context.beginPath();           // start a new path
context.moveTo (25, 25);       // go to (25,25), which now becomes the current point in the path 
context.lineTo (25, 125);      // add a line from (25,25) to (25,125); the latter is the new current point in the path
context.lineTo (125, 125);     // add another line from (25,125) to (125,125) to the path
context.fill();                // the path is closed with a line from (125,125) to the initial (25,25) and 
                               // the shape area is now drawn with the fillStyle color 'lime'
// the second triangle
context.beginPath();           // start another path
context.moveTo (175, 25);      // go to (175,25)
context.lineTo (175, 125);     // add a line from (175,25) to (175,125)
context.lineTo (275, 125);     // add a line from (175,125) to (275,125)
context.fillStyle = 'maroon';  // set the fillStyle to the color 'maroon'
context.fill();                // close the path, making it a triangle, and draw the shape area in 'maroon' color
Komplexeres Formbeispiel

Angenommen, wir erstellen einen Pfad mit ein paar Zeilen

context.beginPath();
context.moveTo (50,25);   
context.lineTo (50,125);
context.lineTo (150,25);  
context.lineTo (150,125);
context.lineTo (250,25);  
context.lineTo (250,125);

Wir können diesen Pfad visualisieren ( stroke()natürlich durch Aufrufen ) und es sieht so aus 24

Beachten Sie jedoch, dass, wenn wir diese Zeilen zum Code hinzufügen

context.fillStyle = 'red';
context.fill();

wir nicht bekommen dieses Bild (von einem konvexen Polygon)

aber diese ( konkave ) stattdessen

Denken Sie daran, dass Aufruf fill()bedeutet, dass der Pfad zuerst geschlossen wird, dh der Endpunkt (250,125)ist (50,25)wie folgt mit dem Anfangspunkt verbunden :

(Natürlich wurde diese Leinwand durch Aufrufen von closePath()und erstellt stroke().)

Und mit dieser Ansicht des geschlossenen Pfades wird deutlich, was der fill()Aufruf bewirkt: Er füllt alle geschlossenen Bereiche der erstellten Form aus.

2.2.9.5 lineTo (x, y)

CanvasRenderingContext2D.lineTo (x, y)

Fügt dem Pfad vom aktuellen Punkt zum neuen Punkt eine Linie hinzu (x,y).

Ein Polygon zeichnen

Ein typisches Beispiel für die Verwendung von lineTo()ist die Konstruktion eines Polygons . Zeichnen wir ein Dreieck, das einfachste aller Polygone. Dieser Code

context.strokeStyle = 'navy'; // set the strokeStyle color to 'navy' (for the stroke() call below)
context.lineWidth = 3.0;      // set the line width to 3 pixels
context.beginPath();          // start a new path
context.moveTo (150,30);      // set (150,20) to be the starting point
context.lineTo (270,120);     // line from (150,30) to (270,120)
context.lineTo (30,120);      // horizontal line from (270,120) to (30,120)
context.lineTo (150,30);      // line back to the starting point (we could have called closePath() instead)
context.stroke();             // actually draw the triangle shape in 'navy' color and 3 pixel wide lines

erzeugt dieses Bild

Beachten Sie, dass beim letzten Aufruf von lineTo()im Quellcode erneut eine Linie von der linken Ecke zum anfänglichen oberen Punkt des Dreiecks gezogen wird. Wir hätten diese Codezeile durch einen Aufruf von ersetzen können context.closePath(), da closePath()dies für uns zum Ausgangspunkt zurückkehrt. 25

Gefülltes Polygon

lineTo()zeichnet keine Linien, das Zeichnen erfolgt mit dem stroke()Aufruf. Wir hätten auch die fill()Methode zur Erzeugung dieses Bildes aufrufen können

Eigentlich lautet der Quellcode wie folgt:

context.fillStyle = 'navy';   // set the fillStyle color to 'navy' (for the fill() call below)
context.beginPath();          // start a new path
context.moveTo (150,30);      // set (150,20) to be the starting point
context.lineTo (270,120);     // line from (150,30) to (270,120)
context.lineTo (30,120);      // horizontal line from (270,120) to (30,120)
context.fill();               // actually draw the triangle shape in 'navy' color and 3 pixel wide lines

Beachten Sie, dass wir weder einen lineTo()Anruf für eine Linie von der linken Ecke (30,120)zur ersten oberen Ecke verwendet haben (150,30), noch closePath()am Ende des Pfads angerufen haben . Dies wäre überflüssig gewesen, da fill()der Pfad automatisch geschlossen wird.

2.2.9.6 moveTo (x, y)

CanvasRenderingContext2D.moveTo (x, y)

Verschiebt den Pfad zum neuen Punkt (x,y). Anders als lineTo(x,y)beim moveTo(x,y)Aufruf wird keine Linie zum neuen Punkt erstellt.

Beispiel

Dieser Code-Ausschnitt

// set the line style to be drawn with stroke()
context.lineWidth = 9.0;
context.strokeStyle = 'red';
// create the path
context.beginPath();
context.moveTo (50,25);   context.lineTo (50,75);    // first vertical line
context.moveTo (100,25);  context.lineTo (100,75);   // second vertical line
context.moveTo (150,25);  context.lineTo (150,75);   // third vertical line
context.stroke();

erzeugt dieses Bild

Aber wenn wir jeden der drei moveTo()Aufrufe durch lineTo()Aufrufe im Code ersetzt hätten, wäre das Bild wie folgt:

2.2.9.7 rect (x, y, w, h)

CanvasRenderingContext2D.rect (x, y, w, h)

Erstellt ein Rechteck mit der linken oberen Ecke bei (x,y), das wPixel breit und hPixel hoch ist.

Beachten Sie, dass das Rechteck mit diesem Befehl nicht gezeichnet wird. Daher ist ein nachfolgender stroke()oder fill()Anruf erforderlich.
Beachten Sie auch, dass die beiden Schritte des Erstellens und anschließenden Zeichnens des Rechtecks ??in den beiden Methoden strokeRect()und kombiniert werden fillRect().

Beispiel

Angenommen, wir möchten ein kleines Haus zeichnen

Wir könnten einen Pfad aus sechs getrennten Linien konstruieren. Wir können es aber auch bauen, indem wir so ein Quadrat (mit rect()) und ein Dach (zwei lineTo()Aufrufe) erstellen

context.beginPath();                // start a new path
context.rect (20,60,60,60);         // create the square body of the house
context.moveTo (10,70);             // create the roof by 1. move to the left
context.lineTo (50,10);             //                    2. create the left line
context.lineTo (90,70);             //                    3. create the right line
context.strokeStyle = "black";      // draw the house by 1. set the strokeStyle to 'black'
context.lineWidth = 5.0;            //                   2. increase the default lineWidth to 5.0
context.stroke();                   //                   3. do the actual drawing
Beispiel mit fill()stattstroke()

Natürlich, wenn wir die letzten drei Zeilen des vorherigen Codes durch diese neuen Zeilen ersetzt hätten

var lg = context.createLinearGradient (20,20,100,130);  // create a linear gradient named lg
lg.addColorStop (0, 'yellow');                          // set the color of lg at the beginning (=top) to 'yellow'
lg.addColorStop (1, 'red');                             // set the color of lg at the end (right bottom) to 'red'
context.fillStyle = lg;                                 // set the fillStyle to lg
context.fill();                                         // draw the house with the fillStyle settings

wir hätten dieses Bild erhalten

Das eigentliche Zeichnen erfolgt mit fill()statt stroke()und dementsprechend erfolgt dies nicht in strokeStyleaber fillStyle. Und diesmal fillStyleist es nicht nur eine gewöhnliche Farbe, sondern ein linearer Farbverlauf (siehe createLinearGradient()undaddColorStop() ).

Neu definieren rect()

Es ist möglich, die rect()Methoden zu ersetzen , indem Sie das Rechteck mit ein paar Linien erstellen. Genauer gesagt, diese eine Zeile

context.rect (x, y, w, h);

kann nur als Abkürzung für diese sechs Zeilen angesehen werden

context.beginPath();         // start a new path
context.moveTo (x,y);        // move to the top left corner as starting point
context.lineTo (x+w, y);     // horizontal line to the top right corner
context.lineTo (x+w, y+h);   // vertical line to the right bottom corner
context.lineTo (x, y+h);     // horizontal line to the left bottom corner
context.closePath();         // vertical line back to the top left corner 

2.2.9.8 quadraticCurveTo (cpx, cpy, x, y)

CanvasRenderingContext2D.quadraticCurveTo (cpx, cpy, x, y)

erzeugt eine gekrümmte Linie vom aktuellen Punkt 0 zum neuen Punkt 1 bei (x,y), bestimmt durch einen Kontrollpunkt cp bei (cpx,cpy). Die Kurve zeigt zunächst bei 0 in die Richtung von cp und bewegt sich schließlich von cp zu Punkt 1 .

Der vollständige Titel dessen, was die Methode hier als "quadratische Kurve" bezeichnet, lautet " quadratische Beziér-Kurve ". Und die "Beziér-Kurve" aus der nächsten Methode (siehe bezierCurveTo()unten) ist eine " kubische Beziér-Kurve " in der richtigen Termininologie. Der Wikipedia-Artikel über Bézier-Kurven enthält sehr schöne und intuitive Animationen zum Erstellen von Bézier-Kurven .

Beispiel

Dieser Code-Ausschnitt

context.lineWidth = 5.0;
context.strokeStyle = 'red';
context.beginPath();
context.moveTo (30,120);                       
context.quadraticCurveTo (210, 30, 210, 120);
context.stroke(); 

erzeugt dieses Bild

Mit moveTo(30,120)dem aktuellen Punkt befindet sich 0 bei (30,120). Durch den Aufruf quadraticCurveTo(210,30,210,120), der Kontrollpunkt cp ist (210,30)und der neue Punkt 1 ist (210,120).

Die rote Kurve beginnt bei 0 und bewegt sich in Richtung cp (geometrisch gesehen: Die Linie von 0 nach cp ist eine Tangente an die Kurve am Punkt 0 ) und biegt sich dann in Richtung 1 (wieder: Die Linie von cp nach 1 ist eine Tangente an die Kurve bei Punkt 1 ).

Beispiel einer Formkonstruktion mit quadratischen Kurven

Angenommen, wir versuchen, eine Ellipse zu zeichnen, die schließlich so aussehen sollte 26

Offensichtlich sind die zwei horizontalen und zwei vertikalen Linien im nächsten Bild Tangenten der Ellipse

und wir können die Ellipse im Wesentlichen als eine Folge von vier quadraticCurveTo()Aufrufen zusammensetzen.

Wir beginnen am obersten Punkt 0 und rufen quadraticCurveTo()zum ersten Mal über den Kontrollpunkt cp1 bis Punkt 1 an :

Der entsprechende Code ist dies

context.moveTo (150,25);                     // move to point 0 at (150,25)
context.quadraticCurveTo (275,25,275,75);    // curve from point 0 to point 1 at (275,75)

Wir wiederholen diesen Schritt noch dreimal.

Das heißt, wir fügen drei weitere quadraticCurveTo()Aufrufe hinzu und wickeln die Teile zwischen a beginPath()und stroke()call ein, sodass der vollständige Code für die ursprüngliche Form dies ist

context.lineWidth = 5.0;                        // set the line width for the stroke() call below
context.strokeStyle = 'red';                    // set the color to 'red' for the stroke() call below
context.beginPath();                            // start the new path
context.moveTo (150,25);                        // move to point 0 at (150,25)
context.quadraticCurveTo (275,25,275,75);       // curve from point 0 to point 1 at (275,75)
context.quadraticCurveTo (275,125,150,125);     // curve from point 1 to point 2 at (150,125)
context.quadraticCurveTo (25,125,25,75);        // curve from point 2 to point 3 at (25,75)
context.quadraticCurveTo (25,25,150,25);        // curve from point 3 back to point 0
context.stroke();                               // draw the shape defined by the current path
Exkurs: quadraticCurveTo()als Sonderfall vonbezierCurveTo()

Theoretisch ist die quadraticCurveTo()Methode nur ein Sonderfall von bezierCurveTo(), der zwei statt eines Kontrollpunktes hat. Man muss nur den gleichen Kontrollpunkt noch einmal wiederholen, dh wir könnten einfach jeden ersetzen

quadraticCurveTo (cpx, cpy, x, y)

Rufen Sie an

bezierCurveTo (cpx, cpy, cpx, cpy, x, y)

Diese Möglichkeit könnte auch interessant sein, da die Firefox 1.5-Implementierung der quadraticCurveTo()Methode einen Fehler aufweist . Dies wird im Canvas-Tutorial des Mozilla Development Network erwähnt , das auch eine Fehlerumgehung bietet.

Tatsächlich scheinen die Browser-Anbieter dies jedoch nicht so zu implementieren, wie Sie möglicherweise in Ihrem eigenen Browser feststellen, wenn Sie die folgenden beiden Bilder vergleichen. Betrachten wir noch einmal das Ellipsenbeispiel, das zuerst quadraticCurveTo()wie zuvor mit Aufrufen implementiert wurde . Dieser Ausschnitt

context.beginPath();
context.moveTo (150,25);
context.quadraticCurveTo (275,  25, 275,  75);    
context.quadraticCurveTo (275, 125, 150, 125);    
context.quadraticCurveTo ( 25, 125,  25,  75);    
context.quadraticCurveTo ( 25,  25, 150,  25);    
context.stroke();        

erzeugt diese Ansicht in Ihrem Browser

Vergleichen Sie das mit der Implementierung mit bezierCurveTo()Aufrufen

context.beginPath();
context.moveTo (150,25);
context.bezierCurveTo (275,  25, 275,  25, 275,  75);    
context.bezierCurveTo (275, 125, 275, 125, 150, 125);    
context.bezierCurveTo ( 25, 125,  25, 125,  25,  75);    
context.bezierCurveTo ( 25,  25,  25,  25, 150,  25);    
context.stroke();        

dieses Bild zu produzieren

2.2.9.9 bezierCurveTo (cp1x, cp1y, cp2x, cp2y, x, y)

CanvasRenderingContext2D.bezierCurveTo (cp1x, cp1y, cp2x, cp2y, x, y)

Erstellt eine gekrümmte Linie vom aktuellen Punkt 0 im Pfad zum neuen Punkt 1 bei (x,y). Die beiden Kontrollpunkte cp1 bei (cp1x,cp1y)und cp2 bei (cp2x,cp2y)bestimmen die tatsächliche Form der Linie: Wenn die Kurve von 0 aus in Richtung cp1 geht und schließlich in 1 eingeht , kommt die Kurve aus der Richtung von cp2 heraus .

Beispiel

Betrachten wir den folgenden Ausschnitt

context.beginPath();
context.moveTo (30,120);                              // go to point 0 at (30,120)
context.bezierCurveTo (120, 30, 240, 30, 240, 120);   // curve from point 0 to point 1 at (240,120) 
context.stroke();  

Dadurch wird die folgende rote Kurve erstellt

Die Kurve beginnt in Punkt 0 in Richtung cp1 und endet in Punkt 1, der von cp2 kommt .

Das gleiche Prinzip gilt, wenn wir die Position der beteiligten Punkte ändern. Zum Beispiel

Beachten Sie auch, dass selbst das "Ziehen" von cp1 von Punkt 0 (oder cp2 von 1 weg ) an der Kurve zieht und sie stärker biegt:

Wie bereits erwähnt, lautet der eigentliche Titel der hier als "Beziér-Kurve" bezeichneten Methode " kubische Beziér-Kurve ". Und die "quadratische Kurve" aus der vorherigen Methode (siehe quadraticCurveTo()oben) ist tatsächlich eine " quadratische Beziér-Kurve " in mathematischen Begriffen. Sie sollten sich unbedingt die intuitiven Animationen im entsprechenden Wikipedia-Eintrag ansehen, insbesondere den Abschnitt zum Erstellen von Bézier-Kurven .

Beispiel: Ein Herz zeichnen

Die gesamte Theorie zu Beziér-Kurven wurde in Autofabriken entwickelt, um in der Praxis glatte Karosserien herzustellen. Das Finden des Codes für eine bestimmte Form von Hand kann jedoch eine entmutigende Aufgabe sein. Lassen Sie uns eine einfache Methode anwenden, wie dies getan werden kann. Angenommen, wir versuchen, den Code für beispielsweise die Form eines Herzens zu finden: 27

Wir beginnen mit der Identifizierung einiger signifikanter Punkte 0 , 1 , 2 , 3 , 4 , 5 und der Tangenten (grün angezeigt), dh der Linien, die die rote Form an diesem bestimmten Punkt berühren:

Dann lesen wir die bezierCurveTo()Anrufe von jedem Stück im Pfad ab. Manchmal müssen wir ein wenig mit der besten Position der Kontrollpunkte auf der Tangente spielen, dh wir können wie zuvor beschrieben etwas ziehen, um die rote Linie in die beste Form zu bringen. Wir erhalten die folgenden sechs Ergebnisse:

Wir sammeln schließlich nur die Teile und erhalten den Code für das ursprüngliche Leinwandbild

context.fillStyle = 'red';
context.beginPath();
context.moveTo (150,60);                        // start at point 0
context.bezierCurveTo (150,30, 100,30, 90,30);  // from point 0 to point 1
context.bezierCurveTo (60,30,30,60,30,90);      // from point 1 to point 2
context.bezierCurveTo (30,180,90,210,150,240);  // from point 2 to point 3 
context.bezierCurveTo (210,210,270,180,270,90); // from point 3 to point 4 
context.bezierCurveTo (270,60,240,30,210,30);   // from point 4 to point 5
context.bezierCurveTo (180,30,150,30,150,60);   // from point 5 to point 0
context.fill();   

2.2.9.10 arc (x, y, r, start, end, anticlockwise)

CanvasRenderingContext2D.arc (x, y, r, start, end, anticlockwise)

definiert ein Stück eines Kreises. Der Mittelpunkt dieses Kreises ist (x,y)der Radius r. Der startund der endPunkt des Bogens werden als Winkel im Bogenmaß angegeben. Der optionale boolesche anticlockwiseParameter definiert, ob die Bögen gegen den Uhrzeigersinn (Wert true) oder im Uhrzeigersinn (Wert false, der Standard ist) gemessen werden . Ein Aufruf von arc()ist Teil einer Pfaddeklaration. Das eigentliche Zeichnen erfolgt mit einem Aufruf von stroke(), Zeichnen fill()eines Teils der Kreislinie oder Zeichnen eines Abschnitts der Kreisscheibe.

Beispiel: fünf Bögen

Zeichnen wir zum Beispiel Abschnitte von fünf Kreisscheiben mit jeweils einem 50Pixelradius

welches vom Code-Snippet generiert wird

context.fillStyle = "rgba(255,0,0,0.33)";    // red color with 1/3 transparency
// now draw five filled circle pieces:
context.beginPath(); context.arc ( 60, 60, 50, 0, 2 * Math.PI, false); context.fill();    // 1st
context.beginPath(); context.arc (180, 60, 50, 0,     Math.PI, false); context.fill();    // 2nd
context.beginPath(); context.arc (300, 60, 50, 0,     Math.PI, true ); context.fill();    // 3rd
context.beginPath(); context.arc (420, 60, 50, 2,           6, false); context.fill();    // 4th
context.beginPath(); context.arc (540, 60, 50, 2,           6, true ); context.fill();    // 5th

Und wenn wir fillStyledurch strokeStyleund jedes Vorkommen von fill()durch ersetzen stroke(), erhalten wir dieses Bild von fünf Kreisbögen:

Fügen wir das Koordinatensystem und einen roten Punkt für die Mitte jedes Bogens hinzu, damit wir ihre genauen Positionen sehen können:

Erläuterung der Parameter

Jeder Anruf von

arc (x, y, r, start, end, anticlockwise)

definiert einen Kreis mit Mittelpunkt (x,y)und Radius r.

Jeder Winkel awird von der 3-Uhr-Position des gegebenen Kreises im Uhrzeigersinn gemessen:

Es ist zu beachten, dass die Einheit jedes Winkels aim Bogenmaß angegeben ist rad , dh der Winkel ist a radund die Länge des Bogens ist a * r.

Der 4. Bogen des Anfangsbeispiels, gezeichnet von, arc(420,60,50,2,6,false)hatte einen Startwinkel von 2 radund einen Endwinkel von6 rad

Und wenn wir den fünften Parameter falsein ändern true, dh wenn wir aufrufen arc(420,60,50,2,6,true), so dass die Richtung auf dem Kreis nicht im Uhrzeigersinn, sondern gegen den Uhrzeigersinn ist, erhalten wir den fünften Bogen

Bogenmaß und Grad

Die meisten Benutzer kennen den Grad ° anstelle des Bogenmaßes radals Winkeleinheit besser . Denken Sie daran, dass ein vollständiger Kreis 2?(ungefähr 6.283) ist und dass dies ?in JavaScript durch den konstanten Wert gegeben ist

Math.PI = 3.141592653589793

Wir konvertieren zwischen Bogenmaß und Grad unter Verwendung der Gleichung hin und her 360°=2?

Im ersten Beispiel war der 1. Bogen ein voller Kreis, und wir zeichnen diesen mit Startwinkel 0und Endwinkel 2*Math.PI. Der 2. und 3. waren Halbkreise vom Startwinkel 0zum Endwinkel Math.PI, die im Uhrzeigersinn bzw. gegen den Uhrzeigersinn gezeichnet wurden.

Hinweis: Die Koordinaten eines Punktes auf einem Kreis

Aus der Trigonometrie geht hervor, dass ein Punkt auf einem Kreis mit Mittelpunkt (x,y), Radius rund einem Winkel von adie Koordinaten hat

(r cos(a) + x, r sin(a) + y)

In JavaScript ist dies gegeben durch

(r * Math.cos(a) + x, r * Math.sin(a) + y)

Zum Beispiel der schwarze Punkt in diesem Bild

mit a= 135°= 3/4?hat die Koordinaten

(60*Math.cos(3/4*Math.PI)+80, 60*Math.cos(3/4*Math.PI)+80)  ===  (37.573593128807154, 122.42640687119285)
arc() als ein Stück im aktuellen Pfad

Ein Aufruf von arc()ist Teil des aktuellen Pfads. Dies hängt vom aktuellen Punkt ab, bevor er aufgerufen wurde, und der Endpunkt des Bogens wird danach zum neuen aktuellen Punkt. Zum Beispiel dieser Code

context.lineWidth = 3.0;
context.strokeStyle = "red";
context.beginPath ();
context.moveTo (30, 90);
context.arc (210, 90, 60, 2, 6);
context.lineTo (390, 90);
context.stroke();  

erzeugt dieses Bild

Der Anfangspunkt des Pfades ist (30,90). Der Aufruf von arc()definiert diesen Bogen, aber auch die Linie vom Anfang (30,90)bis zum Startpunkt des Bogens. Nach dem Aufruf ist der neue aktuelle Punkt der Endpunkt des Bogens. Und der letzte Aufruf von lineTo()zieht die Linie vom Bogen nach(390,90)

Wenn Sie dieses Verhalten und die zusätzlichen Zeilen verhindern möchten, schließen Sie den arc()Aufruf zwischen a beginPath()und stroke()oder ein fill(), wie wir es bisher in allen unseren Beispielen getan haben. Dadurch wird sichergestellt, dass nur der Bogen selbst gezeichnet wird.

Betrachten wir noch einmal unser erstes Beispiel-Canvas-Beispiel mit fünf Bögen

Denken Sie daran, dass wir jeden arc()Anruf in eine Sequenz eingeschlossen haben beginPath(); arc(); stroke();. Wenn wir alle fünf arc()Aufrufe in nur einen Pfad eingeschlossen hätten, dh wenn dies unser Code gewesen wäre

context.beginPath(); 
context.arc ( 60, 60, 50, 0, 2 * Math.PI, false);   // 1st
context.arc (180, 60, 50, 0,     Math.PI, false);   // 2nd
context.arc (300, 60, 50, 0,     Math.PI, true );   // 3rd
context.arc (420, 60, 50, 2,           5, false);   // 4th
context.arc (540, 60, 50, 2,           5, true );   // 5th
context.stroke(); 

Das Bild würde so aussehen

Der Start- und Endpunkt des Bogens werden Teil der Pfaddefinition. Gleiches gilt im Allgemeinen für gefüllte Objekte und mit Zeichenmethoden innerhalb des Pfades.

Nehmen wir als letztes Beispiel diese Leinwand mit einem einzigen Bogen

durch diesen Code generiert

context.beginPath(); 
context.arc (120, 60, 50, 2, 5, true);
context.fill(); 

Wenn wir einen Punkt hinzufügen, sagen wir (30,25)vor dem Bogen, und wenn wir danach eine Linie zeichnen, sagen (30,100)wir zu , dh wenn wir diesen Code verwenden

context.beginPath(); 
context.moveTo (30,25);
context.arc (120, 60, 50, 2, 5, true);
context.lineTo (30,100);
context.fill(); 

Die Leinwand sieht so aus

Der Pfad selbst, der sichtbar ist, wenn wir den Code im Code filldurch ersetzen stroke, ist dieser

2.2.9.11 arcTo (x1, y1, x2, y2, radius)

CanvasRenderingContext2D.arcTo (x1, y1, x2, y2, radius)

Zeichnet einen Bogen mit dem Gegebenen radius, abhängig vom aktuellen Punkt im Pfad und den beiden angegebenen Punkten (x1, y1)und (x2, y2). Weitere Einzelheiten zu dieser Methode finden Sie in den folgenden Beispielen und in der Beschreibung.

Sei contextder 2D-Kontext einer Leinwand mit Standardgröße (das lineWidthist auf gesetzt 5.0, das strokeStyleist 'red'). Dann das folgende Code-Snippet

context.beginPath();
context.moveTo (60, 120); 
context.arcTo (150, 30, 240, 120, 50);
context.stroke();

erzeugt dieses Bild

Die von gezeichnete Form arcTo (x1, y1, x2, y3, radius)hängt von drei Punkten ab:

  1. Der aktuelle Punkt (x0,y0)im Pfad, im Beispiel (60,120)der Status nach dem moveTo(60,120)Aufruf.

  2. Punkt (x1,y1), hier: (150,30)und

  3. (x2,y2), was (240,120)in unserem Beispiel ist.

Die Position dieser drei Punkte auf der Leinwand ist folgende:

Stellen Sie sich nun zwei halb-unendliche Linien mit Punkt 1 als Ursprung vor, von denen eine durch Punkt 0 und die andere über Punkt 1 verläuft.

Das Zeichnen arcTo (x1, y1, x2, y3, radius)zeichnet nun eine Linie, die ihren Ursprung in Punkt 0 hat, in Richtung Punkt 1 verläuft und sich in Richtung Punkt 2 dreht, sodass dies radiusder tatsächliche Radius des Bogens ist. Der Endpunkt der arcTo()Linie ist der Punkt, an dem der Bogen auf die Tangentenlinie trifft (von Punkt 1 zu Punkt 2).

Die ganze Idee wird wahrscheinlich offensichtlicher, wenn wir dasselbe Code-Snippet verwenden

context.beginPath();
context.moveTo (60, 120); 
context.arcTo (150, 30, 240, 120, radius);  // different values for radius 
context.stroke();

und variieren Sie einfach die radius. Die resultierenden Bilder sind diese:

2.2.9.12 clip()

CanvasRenderingContext2D.clip()

schneidet einen Bereich beliebiger Form und Größe von der Leinwand ab.

Beispiel

Angenommen, wir haben eine Zeichnung auf einer Leinwand, beispielsweise ein Rechteck mit Text

und nehmen wir an, wir möchten von dieser Leinwand den Teil abschneiden, der durch diesen Kreis definiert ist

so dass das Gesamtergebnis so aussieht

Die Standardmethode, um dies zu erreichen, besteht aus drei Schritten: 1. Zeichnen Sie die Form des zu beschneidenden Bereichs, 2. Rufen Sie die clip()Methode auf und 3. Zeichnen Sie den Canvas-Inhalt

Der Quellcode des vorherigen Beispiels lautet

// 1. clipped circle area
context.fillStyle = 'yellow';
context.beginPath();
context.arc (60, 60, 60, 0, 2*Math.PI);
context.fill();
// 2. clip
context.clip();
// 3. draw a rectangle with text
context.fillStyle = 'aqua';
context.fillRect (30, 60, 180, 60);
context.fillStyle = 'red';
context.font = '60px sans-serif';
context.fillText ('Hello!', 30, 110);
Ein anderes Beispiel

Im vorherigen Beispiel waren sowohl der abgeschnittene Kreisbereich als auch die Leinwandzeichnungen gefüllte Formen. Aber wir können jede dieser Zahlen durch Strichzahlen ersetzen. Wenn wir beispielsweise jedes Vorkommen eines " fill" durch ein " stroke" im vorherigen Code ersetzen , ist dies das Bild

Bildbeispiel

Die Clip-Technik funktioniert für alle Leinwandzeichnungen, einschließlich Bilder. Um eine Scheibe aus einem Pferdebild auszuschneiden, können wir diesen Code 28 verwenden

// clipped circle area
context.beginPath();
context.arc (60, 60, 60, 0, 2*Math.PI);
context.fill();
// clip
context.clip();
// insert the image
var image = new Image();
image.src = "horse.jpg";
context.drawImage (image, 0, 0, 120, 120); // insert the image at (0,0) on an area of 120x120 pixels

Das Ergebnis ist dies

2.2.9.13 isPointInPath (x, y)

CanvasRenderingContext2D.isPointInPath (x, y)

Gibt zurück, truewenn sich der angegebene Punkt (x,y)im aktuellen Pfad befindet, falseandernfalls.

Zum Beispiel dieses Code-Snippet

// add a new path
context.beginPath();
context.moveTo (75,130);                // make (75,130) the current point
context.lineTo (145,75);                // line from (75,130) to (145,75)
context.arc (75,75,70,0,Math.PI,true);  // draw half circle disk with center (75,75), radius 70 and counterclockwise
context.lineTo (75,130);                // line from (5,70) to (75,130)
context.lineWidth = 3.0;                // set the line width for the stroke drawing
context.strokeStyle = 'purple';         // set the line color for the stroke drawing
context.stroke();                       // draw the shape

// determine the position of two points
var answer1 = context.isPointInPath (25,100);    // answer1 is now either true or false
var answer2 = context.isPointInPath (100,25);    // answer2 is also either true or false

// print out the result on the canvas    
context.font = "14pt sans-serif";                                        // set the font for the text
context.fillText ( "isPointInPath(25,100)  is  " + answer1, 200, 50);    // print the first line of text
context.fillText ( "isPointInPath(100,25)  is  " + answer2, 200, 100);   // print the second line of text

erzeugt dieses Leinwandbild

2.2.10 Text

Text wird mit einer der beiden folgenden Methoden zum Canvas (Kontext) hinzugefügt:

Das tatsächliche Rendern des Textes hängt auch von den aktuellen Werten der folgenden Eigenschaften ab:

2.2.10.1 font

CanvasRenderingContext2D.font

Legt die Schriftart (dh den Stil, die Größe usw.) fest, mit der Text zur Zeichenfläche (Kontext) hinzugefügt wird. Sein Wert ist ein beliebiger Wert der CSS3- fontEigenschaft (siehe CSS-Schriftarten unten). Der Standardwert fontist '10px sans-serif'. Werte, die nicht als CSS-Schriftwerte analysiert werden können, werden ignoriert. Relative Schlüsselwörter und Längen werden relativ zur Schriftart des Canvas-Elements berechnet.

Auf der folgenden Zeichenfläche wird in jeder Zeile der fontWert auf einen anderen Wert gesetzt, und dieser Wert wird strokeText()links und fillText()rechts mit angezeigt :

Der Quellcode für das vorherige Leinwandbild lautet wie folgt:

// set both the strokeStyle and the fillStyle to black
context.strokeStyle = 'black';
context.fillStyle = 'black';
// first line of text in the default font:
context.strokeText(context.font, 10, 20);  
context.fillText (context.font, 350, 20);
// second line of text:
context.font = '20px fantasy';
context.strokeText(context.font, 10, 40);  
context.fillText (context.font, 350, 40);
// third line of text:
context.font = '40px Verdana';
context.strokeText(context.font, 10, 80);  
context.fillText (context.font, 350, 80);
// fourth line of text:
context.font = '60px Arial';
context.strokeText(context.font, 10, 140);  
context.fillText (context.font, 350, 140); 

2.2.10.2 textAlign

CanvasRenderingContext2D.textAlign

Legt die horizontale Ausrichtung von Text fest (ähnlich der CSS- text-alignEigenschaft). Seine Werte ist eine der folgenden: 'start', 'end', 'left', 'right'oder 'center'. Der Standardwert ist 'start'.

In jedem der folgenden Beispiele contextist der 2d-Kontext des angegebenen Canvas-Elements angegeben.

  1. Berufung

    context.textAlign = 'left';
    context.fillText ("Hello world!", 300, 30);</pre>

    sieht aus wie das:

  2. Berufung

     context.textAlign = 'right';
     context.fillText ("Hello world!", 300, 30);

    sieht aus wie das:

  3. Berufung

     context.textAlign = 'center';
     context.fillText ("Hello world!", 300, 30);

    sieht aus wie das:

Das tatsächliche Erscheinungsbild des Textes, wenn er eingestellt textAlignist 'left'oder 'right'von der Richtung des Textes abhängt. Diese Richtung wird im dirAttribut definiert , entweder 'ltr'(von links nach rechts) oder 'rtl'(von rechts nach links), und das Canvas-Element erbt diesen Wert.

2.2.10.3 textBaseline

CanvasRenderingContext2D.textBaseline

stellt die vertikale Ausrichtung von Text und weist einen der folgenden Werte: 'top', 'hanging', 'middle', 'alphabetic', 'ideographic'oder 'bottom'. Der Standardwert ist 'alphabetic'.

Das folgende Bild (aus dem W3.org-Text auf dem Canvas-Element entnommen ) erklärt die verschiedenen Zeilen, die beim Schreiben eine Rolle spielen :

 

Dieses Skript

<canvas id="TextBaselineSample" width=750 height=100></canvas>
<script>
  var context = document.getElementById('TextBaselineSample').getContext('2d');
  context.addGrid (50);
  context.font = '20px monospace';  
  context.textBaseline = 'top';          context.fillText ( "top",         0,   50);
  context.textBaseline = 'hanging';      context.fillText ( "hanging",     100, 50);
  context.textBaseline = 'middle';       context.fillText ( "middle",      200, 50);
  context.textBaseline = 'alphabetic';   context.fillText ( "alphabetic",  300, 50);
  context.textBaseline = 'ideographic';  context.fillText ( "ideographic", 450, 50);
  context.textBaseline = 'bottom';       context.fillText ( "bottom",      600, 50);  
</script>

sieht so aus

2.2.10.4 fillText (text, x, y) undfillText (text, x, y, maxWidth)

CanvasRenderingContext2D.fillText (text, x, y)

und

CanvasRenderingContext2D.fillText (text, x, y, maxWidth)

Zeichnen Sie die angegebene textZeichenfolge an der (x,y)Position in gefüllten Zeichen auf der Leinwand. Wenn die Option (Gleitkommazahl) festgelegt maxWidthist, wird der Text so komprimiert, dass er in diese Grenze passt.
Standardmäßig xist dies der startPunkt der textZeichenfolge (siehe textAlign ) und ydie alphabeticGrundlinie (siehe textBaseline ).

Zum Beispiel das folgende Code-Snippet

context.fillStyle = 'black';         // explicitly sets the text color to (default) 'black'
context.font = '50px monospace';
context.fillText ("Hello world!", 0, 50);  
context.fillText ("This is a longer string that is limited to 750 pixel.", 0, 100, 750);  
context.fillText ("This is a longer string that is limited to 300 pixel.", 0, 150, 300);  

rendert wie folgt

2.2.10.5 strokeText (text, x, y) undstrokeText (text, x, y, maxWidth)

CanvasRenderingContext2D.strokeText (text, x, y, maxWidth)

und

CanvasRenderingContext2D.strokeText (text, x, y, maxWidth)

Zeichnen Sie die angegebene textZeichenfolge an der (x,y)Position in Strichzeichen auf der Leinwand. Wenn die Option (Gleitkommazahl) festgelegt maxWidthist, wird der Text entsprechend skaliert.
Standardmäßig xist dies der startPunkt der textZeichenfolge (siehe textAlign ) und ydie alphabeticGrundlinie (siehe textBaseline ).

(Laut der Referenz auf w3schools.com wird die maxWidthEigenschaft vom Safari-Browser nicht unterstützt.)

Zum Beispiel dieser Code

context.strokeStyle = 'black';        // explicitly sets the text color to (default) 'black'
context.lineWidth = 2.0;              // double of the default lineWidth
context.font = '50px monospace';
context.strokeText ("Hello world!", 0, 50);  
context.strokeText ("This is a longer string that is limited to 750 pixel.", 0, 100, 750);  

sieht wie folgt aus

2.2.10.6 measureText(text).width

CanvasRenderingContext2D.measureText(text).width

Gibt die Breite der textZeichenfolge in den aktuellen Schriftarteinstellungen zurück und wird in Pixel gemessen.

Wenn contextes sich also um den 2d-Kontext der angegebenen context.measure('Hello world!').widthZeichenfläche handelt , wird die Länge der Zeichenfolge "Hello world!"in den aktuellen Einstellungen zurückgegeben.

Die Form dieses Aufrufs ist ziemlich umständlich und besteht natürlich aus zwei Schritten: canvas.measureText(text)Gibt ein TextMetricsObjekt zurück. In der [ WHATWG ] -Spezifikation enthielt ein TextMetricsObjekt ziemlich viele Informationen. All dies ist jedoch im offiziellen [ W3C ] -Standard verdeckt , nur das widthEigentum des TextMetricsObjekts bleibt zugänglich. Durch einen Anruf erhalten canvas.measureText(text).widthwir diese Informationen.

Denken Sie daran, dass die aktuelle Texthöhe von der fontEigenschaft festgelegt und gespeichert wird .

Im folgenden Beispiel wird wder Wert für die Breite der Zeichenfolge "This is some text."in den aktuellen Schriftarteinstellungen (nämlich 30px Arial) verwendet. Wie sich herausstellt, wist 233.

Der Quellcode des vorherigen Beispiels lautet wie folgt:

<canvas id="TextSampleMeasureText" width=700 height=70></canvas>
<script>
  var canvas = document.getElementById('TextSampleMeasureText');
  var context = canvas.getContext('2d');
  context.addGrid (30);
  var text = "This is some text.";
  context.font = '30px Arial'; 
  context.fillText (text, 0, 30);
  var w = context.measureText(text).width;
  context.fillText ("The previous line is " + w + " pixel wide.", 0, 60);
</script>

2.2.11 Bilder zeichnen

Wir können ein Bild mit in die Leinwand einfügen

context.drawImage (image, ...)

Wo contextist das CanvasRenderingContext2DObjekt der gegebenen Leinwand und imageist entweder

  • a HTMLImageElement(z. B. ein JPEG- oder PNG-Dateibild) oder

  • ein anderer HTMLCanvasElementoder sogar

  • a HTMLVideoElement.

Die drawImage(image,...)Methode ist in mehreren Versionen mit zunehmender Flexibilität und Anzahl von Parametern erhältlich. ...Diese werden im Folgenden beschrieben. 29

2.2.11.1 drawImage (image, dx, dy)

CanvasRenderingContext2D.drawImage (image, dx, dy)

zeichnet das imageauf die Leinwand, wo (dx,dy)ist der Punkt seiner linken oberen Ecke.

horse.jpgAngenommen , die Datei ist ein 300 x 425 Pixel großes Bild und contextder 2D-Kontext einer bestimmten 400 x 500 Pixel großen Leinwand. Dann dieser Code

var image = new Image();               // 1. create the image: (a). create an image object
image.src = "horse.jpg";               //                      (b). set it to the jpg file
context.drawImage (image, 50, 50);     // 2. draw the image onto the canvas at (50,50)

erzeugt dieses Bild

2.2.11.2 drawImage (image, dx, dy, dw, dy)

CanvasRenderingContext2D.drawImage (image, dx, dy, dw, dh)

Zeichnet ein imageals Rechteck (dx,dy,dw,dy)in die Leinwand, wobei (dx,dy)die obere linke Ecke dieses Zielrechtecks dwseine Breite und dhHöhe ist.

Wenn wir beispielsweise unser horse.jpgBild erneut aufnehmen und contextes sich um den 2D-Kontext einer 700 x 500 Pixel großen Leinwand handelt, ist der Code

  var image = new Image();
  image.src = "horse.jpg";
  context.drawImage (image, 50, 50, 600, 150);
  context.drawImage (image, 50, 250, 150, 200);
  context.drawImage (image, 350, 250, 100, 200);
  context.drawImage (image, 600, 250, 50, 200);

rendert als

2.2.11.3 drawImage (image, sx, sy, sw, sh, dx, dy, dw, dh)

CanvasRenderingContext2D.drawImage (image, sx, sy, sw, sh, dx, dy, dw, dh)

schneidet ein Rechteck (sx,sy,sw,sh)aus der Quelle heraus imageund fügt es als (dx,dy,dw,dh)in die Zielleinwand ein.

Wenn wir beispielsweise unser Pferdebild horse.jpgerneut verwenden, contextist dies der 2D-Kontext einer 350x350-Leinwand

var image = new Image();
image.src = "horse.jpg";
context.drawImage (image, 150, 40, 130, 120, 75, 100, 200, 150);

erzeugt dieses Bild

Das Rechteck (sx,sy,sw,sh)aus dem Quellbild hat die linke obere Ecke in (sx,sy)Breite swund Höhe sh. Das Rechteck (dx,dy,dw,dh)im Zielbereich hat die linke obere Ecke (dx,dy), Breite dwund Höhe dh.

2.2.12 Pixel Manipulation

Das Bild einer Leinwand kann als Struktur formalisiert werden, die den Farbwert für jedes ihrer Pixel enthält. Dies geschieht durch ein ImageDataObjekt. Wir werden nun zeigen, wie man ein solches Objekt mit erstelltcreateImageData() , wie man ein solches Objekt aus einer Leinwand ausschneidet getImageData()und wie man ein solches Objekt in einen Bereich einer bestimmten Leinwand mit einfügt putImageData().

2.2.12.1 ImageData

Ein ImageData Objekt ist eine formale Darstellung eines Bildes und wird durch drei Eigenschaften angegeben: Die zwei ganzzahligen Werte widthund heightfür seine Größe sowie dataein Array ( CanvasPixelArray), das die Farbwerte für alle Pixel enthält.

Angenommen, wir haben ein Bild (eine Leinwand oder einen Ausschnitt aus einem Leinwandkontext) mit einer bestimmten Breite wund Höhe h. Die Farbe jeden Pixel (x,y)wird durch die gegebene RGBA Farbe , wobei jede der vier Komponenten , , , ist ein Wert aus dem Satz und steht für die Menge an Rot, für Grün, für Blau, und für die „alpha“ oder Transparenzwert . Das gesamte Bild wird dann formal durch eine Tabelle wie diese dargestellt:rgba(R(x,y),G(x,y),B(x,y),A(x,y))R(x,y)G(x,y)B(x,y)A(x,y){0,1,2,...,255}RGBA

x = 0 x = 1 ... x = w-1
y = 0 rgba(R(0,0),G(0,0),B(0,0),A(0,0)) rgba(R(1,0),G(1,0),B(1,0),A(1,0)) ... rgba(R(w-1,0),G(w-1,0),B(w-1,0),A(w-1,0))
y = 1 rgba(R(0,1),G(0,1),B(0,1),A(0,1)) rgba(R(1,1),G(1,1),B(1,1),A(1,1)) ... rgba(R(w-1,1),G(w-1,1),B(w-1,1),A(w-1,1))
y = 2 rgba(R(0,2),G(0,2),B(0,2),A(0,2)) rgba(R(1,2),G(1,2),B(1,2),A(1,2)) ... rgba(R(w-1,2),G(w-1,2),B(w-1,2),A(w-1,2))
... ... ... ...
y = h-1 rgba(R(0,h-1),G(0,h-1),B(0,h-1),A(0,h-1)) rgba(R(1,h-1),G(1,h-1),B(1,h-1),A(1,h-1)) ... rgba(R(w-1,h-1),G(w-1,h-1),B(w-1,h-1),A(w-1,h-1))

In einem ImageDataObjekt werden diese Informationen in diesem Objekt mit drei Eigenschaften gespeichert:

{ 
  Breite: w,
  Höhe: h,
  Daten: [R (0,0) , G (0,0) , B (0,0) , A (0,0) , R (1,0) , G (1,0) , B (1,0) ) , A (1,0) , ..., R (w-1, h-1) , G (w-1, h-1) , B (w-1, h-1) , A (w- 1, h-1) ]
}}

Der dataWert ist ein Array, das alle Farbwerte in einer einzigen Sequenz umfasst. Die Länge dieses Arrays beträgt4 * w * h .

Beispiel

Betrachten wir das folgende Bild, ein Quadrat mit einer Seitenlänge von 256 Pixeln.

Die Farbe jedes Pixels (x,y)wird wie folgt eingestellt:

Zum Beispiel,

Die numerische Tabellendarstellung dieses Bildes ist somit wie folgt gegeben

x = 0 x = 1 ... x = 255
y = 0 rgba(0,0,0,255) rgba(1,0,0,255) ... rgba(255,0,0,255)
y = 1 rgba(0,1,0,255) rgba(1,1,0,255) ... rgba(255,1,0,255)
y = 2 rgba(0,2,0,255) rgba(1,2,0,255) ... rgba(255,2,0,255)
... ... ... ...
y = 255 rgba(0,255,0,255) rgba(1,255,0,255) ... rgba(255,255,0,255)

und dies wird durch ein ImageDataObjekt mit den drei Eigenschaften dargestellt

{
  width : 256,
  height: 256,
  data  : [ 0,0,0,255,   1,0,0,255,   ..., 255,0,0,255,
            0,1,0,255,   1,1,0,255,   ..., 255,1,0,255,
            ...,
            0,255,0,255, 1,255,0,255, ..., 255,255,0,255 ]
}

wo die Länge des dataArrays ist 4*256*256, dh es enthält262144 ganzzahlige Komponenten.

(Informationen zur tatsächlichen Generierung dieses ImageDataObjekts und des vorherigen Leinwandbilds finden Sie in Beispiel 1 unten.)

2.2.12.2 createImageData (sw,sh) undcreateImageData (imagedata)

CanvasRenderingContext2D.createImageData (sw,sh)

Gibt ein ImageDataObjekt mit der angegebenen (Quell-) Breite swund Höhe zurück sh. Alle 4 * sw * shKomponenten im dataArray sind auf eingestellt 0, dh jedes Pixel ist transparent schwarz.

CanvasRenderingContext.2D.createImageData (imagedata)

Gibt ein ImageDataObjekt mit denselben Dimensionen wie das angegebene Argument zurück (das selbst ein ImageDataObjekt ist). Auch hier sind alle Pixel im zurückgegebenen Objekt transparent schwarz.

Der letztere der beiden Anrufe createImageData(imagedata)ist somit nur eine Kurzversion zum Anrufen createImageData(imagedata.width, imagedata.height).

Jeder createImageData()Aufruf gibt somit ein ImageDataObjekt zurück, in dem jedoch alle Array-Elemente des dataArrays festgelegt sind 0. Sobald das Objekt erstellt wurde, können alle diese RGBA-Farbkomponenten durch explizite Verweise auf geändert werden data[i].

Im Folgenden präsentieren wir einige Demonstrationen für diese Techniken.

2.2.12.3 getImageData (sx, sy, sw, sh)

CanvasRenderingContext2D.getImageData(in float sx, in float sy, in float sw, in float sh)

Gibt das zurück ImageData, das den rechteckigen Abschnitt des Gegebenen darstellt CanvasRenderingContext2D, dessen Ursprung darin (sx,sy)liegt, der swPixel breit und shPixel hoch ist.

Im Folgenden zeigen wir anhand einiger Beispiele, wie dies funktioniert.

2.2.12.4 putImageData (imagedata, dx, dy) undputImageData (imagedata, dx, dy, sx, sy, sw, sh)

CanvasRenderingContext2D.putImageData (imagedata, dx, dy)

nimmt ein gegebenes ImageDataObjekt imagedataund fügt es (dx,dy)in das gegebene ein CanvasRenderingContext2D.

CanvasRenderingContext2D.putImageData (imagedata, dx, dy, dx, dy, dw, dh)

macht dasselbe wie der vorherige Funktionsaufruf, aber dieses Mal wird nur der Teil aus dem imagedataBild genommen, der seine linke obere Ecke hat (dx,dy)und dwPixel breit und dhPixel hoch ist.

Codebeispiele und Bilder finden Sie im nächsten Abschnitt .

2.2.12.5 Beispiele für die Pixelmanipulation

Beispiel 1 (die tatsächliche Generation des vorherigen Beispiels)

Betrachten Sie das vorherige Bild noch einmal:

Es wurde von diesem Code-Snippet generiert

<canvas id="ImageDataSample" width=256 height=256> </canvas>
<script>
  var canvas = document.getElementById('ImageDataSample');
  var w = canvas.width;          // w is 256
  var h = canvas.height;         // h is 256
  var context = canvas.getContext('2d');
  context.addGrid(32);
  var imDat = context.createImageData (w,h);
  var i = 0;
  for (var y = 0; y < h; y++) {
    for (var x = 0; x < w; x++) {
      imDat.data[i++] = x;        // the Red component of (x,y), which is set to x
      imDat.data[i++] = y;        // the Green component of (x,y), which is set to y
      imDat.data[i++] = 0;        // the Blue component of (x,y), which is constant 0
      imDat.data[i++] = 255;      // the Alpha/transparency of (x,y), which is constant 255, i.e. fully visible
    };
  };
 context.putImageData (imDat, 0, 0)
</script>

Zuerst erstellen wir ein ImageDataObjekt, es ruft imDat, unter Berufung auf context.createImageData(w,h)mit wund hauf beide gesetzt 256. Zu diesem Zeitpunkt ist das imDat.dataArray ein Array mit einer Länge 4*256*256, wobei jede Array-Komponente eine ist 0. Das Einstellen aller dieser Komponenten auf den erforderlichen Farbcode erfolgt in der Doppelschleife for. Wenn diese Schleife wieder verlassen wird, imDatist es so, wie wir es wollten. Im letzten Schritt context.putImageData(imDat,0,0)fügt der Aufruf dieses ImageDataObjekt mit der linken oberen Ecke bei ein(0,0) in die Zeichenfläche eingefügt.

Beispiel 2 (einfache Kopie eines rechteckigen Abschnitts)

Lassen Sie uns zeigen, wie wir einen rechteckigen Abschnitt ausschneiden und ihn erneut in dasselbe Bild kopieren. Betrachten Sie diese Leinwand

generiert durch dieses Code-Snippet

context.fillStyle = 'black';
context.font = '40pt sans-serif';
context.fillText ('Hi there!',30,70);   

Wir schneiden nun das 260x60Pixelrechteck bei aus (20,20)und platzieren seine Kopie bei (320,20). Wenn wir diese Rechteckigkeit durch einen roten Rahmen anzeigen, wird der gesamte Vorgang als angezeigt

Das eigentliche Kopieren erfolgt durch Hinzufügen der folgenden zwei Zeilen zum vorherigen Code

var imDat = context.getImageData (20,20,260,60); 
context.putImageData (imDat,320,20);     

Mit der ersten Zeile löschen wir ein ImageDataObjekt namens imDat. Mit der zweiten Zeile fügen wir dieses Objekt erneut in das Bild ein.
Das vom vorherigen Code erzeugte Bild ist tatsächlich dieses

Beispiel 3 (ein Spiegelbild eines rechteckigen Abschnitts)

Wir haben gerade eine einfache Kopie eines Canvas-Abschnitts in zwei Schritten erstellt: 1. Holen Sie sich ein ImageDataObjekt mit dem NamenimDat und 2. fügen Sie es erneut in den Canvas-Kontext ein.

Wir ändern nun das erhaltene Objekt, bevor wir es zurücksetzen, und führen drei Schritte aus:

  1. ausgeschnitten imDatwie zuvor mit getImageData(),

  2. Erstellen Sie ein neues ImageDataObjekt mirImDatmit dem Spiegelbild von imDatby

    1. Initialisieren Sie mirImDatmit createImageData()als ImageDataObjekt mit der gleichen Größe wieimDat

    2. Aktualisieren Sie alle Farbkomponenten von mirImDat.data(so dass jede Zeile von imDatumgekehrt wird mirImDat)

  3. setzen mirImDatin das Bild zurück mitputImageData()

Das resultierende Bild ist dies

und der vollständige Quellcode zum Generieren dieser Zeichenfläche lautet wie folgt:

// 0. Generated the text field
context.fillStyle = 'black';
context.font = '40pt sans-serif';
context.fillText ('Hi there!',30,70);     

// 1. Cut out the ImageData object named imDat
var w = 260;                                     // explicitly define the width w of the rectangle area
var h = 60;                                      // explicitly define the height h of the rectangle 
var imDat = context.getImageData(20,20,w,h);     // cut out the rectangle and save it in an ImageData object named imDat

// 2. Create a new ImageDate object with the mirror image, called mirImDat 
var mirImDat = context.createImageData(imDat);   // a. initialze mirImDat; all components of mirImDat.data are 0 here
for (var y = 0; y < h; y++) {                    // b. update the mirImDat.data components
  for (var x = 0; x < w; x++) {
    var d = 4 * (y * w + x);
    var s = 4 * (y * w + (w - (x + 1)));
    mirImDat.data [d + 0] = imDat.data [s + 0];
    mirImDat.data [d + 1] = imDat.data [s + 1];
    mirImDat.data [d + 2] = imDat.data [s + 2];
    mirImDat.data [d + 3] = imDat.data [s + 3];
  };                                              // done updating the mirImDat.data components
};

// 3. Insert the mirImDat into the image again
context.putImageData (mirImDat,320,20);   

Beachten Sie, dass in Schritt 2.a mirImDatdurch initialisiert wird

var mirImDat = context.createImageData(imDat);

Wie bereits erläutert , createImageData(imDat)wird nicht vollständig geklont imDat, sondern nur ein ImageDataObjekt mit denselben Abmessungen erstellt, wobei jedoch alle Farbwerte im dataArray auf festgelegt sind 0. Dementsprechend könnte die vorherige Zeile durch die folgende Zeile ersetzt werden, die genau dasselbe tut:

var mirImDat = context.createImageData(w,y);

In den verschachtelten Schleifen von Schritt 2.b werden die Komponenten von mirImDat.datadann auf die korrekten Werte aktualisiert, die sich mirImDatin das wahre Spiegelbild von verwandelnimDat .

Dieses Beispiel einer Spiegelkopie zeigt, wie alle Transformationsmethoden von CanvasRenderingContext2Ddurch diese Manipulationen auf Pixelebene erneut implementiert werden können.

3 Anhänge

3.1 Referenzzusammenfassung

Das Folgende ist eine Kopie der Schnittstellendefinitionen des HTMLCanvasElement, des CanvasRenderingContext2Dund einiger anderer verwandter Objekte, wie sie im Standard [ W3C.org ] angegeben sind. Die Inhaltstabelle des Referenzteils dieses Canvas-Handbuchs ähnelt den ersten beiden Hauptschnittstellen.

interface HTMLCanvasElement : HTMLElement {
  attribute unsigned long width;
  attribute unsigned long height;
  DOMString toDataURL(optional in DOMString type, in any... args);
  Object getContext(in DOMString contextId);
};
interface CanvasRenderingContext2D {

  // back-reference to the canvas
  readonly attribute HTMLCanvasElement canvas;

  // state
  void save(); // push state on state stack
  void restore(); // pop state stack and restore state

  // transformations (default transform is the identity matrix)
  void scale(in float x, in float y);
  void rotate(in float angle);
  void translate(in float x, in float y);
  void transform(in float m11, in float m12, in float m21, in float m22, in float dx, in float dy);
  void setTransform(in float m11, in float m12, in float m21, in float m22, in float dx, in float dy);

  // compositing
  attribute float globalAlpha;                   // (default 1.0)
  attribute DOMString globalCompositeOperation;  // (default source-over)

  // colors and styles
  attribute any strokeStyle;                     // (default black)
  attribute any fillStyle;                       // (default black)
  CanvasGradient createLinearGradient(in float x0, in float y0, in float x1, in float y1);
  CanvasGradient createRadialGradient(in float x0, in float y0, in float r0, in float x1, in float y1, in float r1);
  CanvasPattern createPattern(in HTMLImageElement image, in DOMString repetition);
  CanvasPattern createPattern(in HTMLCanvasElement image, in DOMString repetition);
  CanvasPattern createPattern(in HTMLVideoElement image, in DOMString repetition);

  // line caps/joins
  attribute float lineWidth;       // (default 1)
  attribute DOMString lineCap;     // "butt", "round", "square" (default "butt")
  attribute DOMString lineJoin;    // "round", "bevel", "miter" (default "miter")
  attribute float miterLimit;      // (default 10)

  // shadows
  attribute float shadowOffsetX;   // (default 0)
  attribute float shadowOffsetY;   // (default 0)
  attribute float shadowBlur;      // (default 0)
  attribute DOMString shadowColor; // (default transparent black)

  // rects
  void clearRect(in float x, in float y, in float w, in float h);
  void fillRect(in float x, in float y, in float w, in float h);
  void strokeRect(in float x, in float y, in float w, in float h);

  // path API
  void beginPath();
  void closePath();
  void moveTo(in float x, in float y);
  void lineTo(in float x, in float y);
  void quadraticCurveTo(in float cpx, in float cpy, in float x, in float y);
  void bezierCurveTo(in float cp1x, in float cp1y, in float cp2x, in float cp2y, in float x, in float y);
  void arcTo(in float x1, in float y1, in float x2, in float y2, in float radius);
  void rect(in float x, in float y, in float w, in float h);
  void arc(in float x, in float y, in float radius, in float startAngle, in float endAngle, in boolean anticlockwise);
  void fill();
  void stroke();
  void clip();
  boolean isPointInPath(in float x, in float y);

  // text
  attribute DOMString font;           // (default 10px sans-serif)
  attribute DOMString textAlign;      // "start", "end", "left", "right", "center" (default: "start")
  attribute DOMString textBaseline;   // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")
  void fillText(in DOMString text, in float x, in float y, optional in float maxWidth);
  void strokeText(in DOMString text, in float x, in float y, optional in float maxWidth);
  TextMetrics measureText(in DOMString text);

  // drawing images
  void drawImage(in HTMLImageElement image, in float dx, in float dy, optional in float dw, in float dh);
  void drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
  void drawImage(in HTMLCanvasElement image, in float dx, in float dy, optional in float dw, in float dh);
  void drawImage(in HTMLCanvasElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
  void drawImage(in HTMLVideoElement image, in float dx, in float dy, optional in float dw, in float dh);
  void drawImage(in HTMLVideoElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);

  // pixel manipulation
  ImageData createImageData(in float sw, in float sh);
  ImageData createImageData(in ImageData imagedata);
  ImageData getImageData(in float sx, in float sy, in float sw, in float sh);
  void putImageData(in ImageData imagedata, in float dx, in float dy, optional in float dirtyX, in float dirtyY, in float dirtyWidth, in float dirtyHeight);

};
interface CanvasGradient {
  // opaque object
  void addColorStop(in float offset, in DOMString color);
};
interface CanvasPattern {
  // opaque object
};
interface TextMetrics {
  readonly attribute float width;
};
interface ImageData {
  readonly attribute unsigned long width;
  readonly attribute unsigned long height;
  readonly attribute CanvasPixelArray data;
};
interface CanvasPixelArray {
  readonly attribute unsigned long length;
  getter octet (in unsigned long index);
  setter void (in unsigned long index, in octet value);
};

3.2 Einige Elemente von CSS

3.2.1 CSS-Farben

CSS-Farben können mit den folgenden Methoden angegeben werden:

  • hexadezimale Farben , angegeben als #RRGGBB, wo RRdie rote bezeichnet, GGdie grüne und BBdie blaue Komponente der Farbe und jeder dieser sechs Buchstaben steht für einen hexadezimalen Wert, das heißt einer der 0, ..., 9, A, ..., F. Zum Beispiel,

    • #0000FFhat keine (= 00) roten, keine grünen und vollen (= FF) blauen Komponenten, mit anderen Worten, dies ist reines Blau.
  • RGB - Farben auf die gleiche Weise , dass hexadezimale Farben tun, aber ihre Syntax ist anders, nämlich rgb(R,G,B), wo R, Gund Bauch die Werte für die Rot-, Grün- und Blau - Komponenten sind. Hier sind die Werte entweder Dezimalzahlen zwischen 0und 255(einschließlich) oder Prozentwerte zwischen 0%und 100%. Zum Beispiel,

    • rgb(0,0,255)ist die Farbe ohne rote, keine grünen und vollblauen Komponenten. Das ist also wieder reines Blau.

    • rgb(0%,0%,100%) ist auch das reine Blau.

  • RGBA Farben werden durch die Form festgelegt rgba(R,G,B,A), wo die R, G, BTeil die gleichen wie in RGB - Farben ist. Der Alpha-Parameter A gibt die Deckkraft an und ist ein Wert zwischen 0.0(vollständig transparent) und 1.0(vollständig undurchsichtig). Zum Beispiel,

    • rgba(100%,0%,0%,0.5) ist eine rein rote Farbe, die halbtransparent ist.
  • HSL-Farben haben die Form hsl(H,S,L), die den Farbton, die Sättigung und die Helligkeit für eine zylindrisch koordinierte Darstellung von Farben angibt. Der Farbton H ist ein Grad im Farbkreis von 0bis 360, wobei 0(oder 360) rot, 120grün und 240blau ist. Die Sättigung S ist ein Prozentwert von 0%bis 100%, wobei 0%ein Grauton bedeutet und 100%die volle Farbe ist. Die Helligkeit L ist auch ein Prozentsatz, 0%ist schwarz und 100%ist weiß. Zum Beispiel

    • hsl(120,65%,75%)
  • HSLA Farben hat die Form hsla(H,S,L,A), in der H, Sund Lsind die gleichen wie in HSL Farben und der alpha - Parameter Adefiniert die Opazität von 0.0(für vollständig transparent) bis 1.0(vollständig undurchsichtig).

  • Vordefinierte oder browserübergreifende Farbnamen sind Farben in HTML und CSS, die durch ihren Namen angegeben werden, z. B. BlueVioletoder DarkBlue. Es gibt

    • 17 Standardfarben: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, whiteund yellow.

    • 130 weitere von AliceBluebisYellowGreen

Links:

3.2.2 CSS-Schriftarten

Weitere Informationen finden Sie in der Standardbeschreibung auf w3.org oder in der Referenz auf w3schools.com .

Schrifteigenschaften in CSS
CSS-Eigenschaft mögliche Werte
font-style normal, italic, oblique, inherit
Standard ist normalund die inheritMittel von dem Mutterelement geerbt.
font-variant normal(Standard), small-caps, inherit
(Standard ist normalund inheritMittel von dem Mutterelement geerbt.)
font-weight normal, bold, lighter, 100, 200, 300, 400, 500, 600, 700, 800, 900, inherit
(Standardeinstellung ist normal, das ist das gleiche wie 400. Und boldist das gleiche wie 700.)
font-size xx-small, x-small, small, medium, large, x-large, xx-large, smaller, larger, inherit
Oder irgendeine Länge wie angegeben nnpx , nncm , nn? ,
font-family Es gibt zwei Arten von Schriftfamiliennamen:
Generika-Familie
nämlich: serif, sans-serif, cursive, fantasyodermonospace
Familienname
zum Beispiel times, courier, arial, verdana,"Times New Roman" usw.
Die font-familyEigenschaft kann mehrere durch Kommas getrennte Werte als "Fallback" -System enthalten. Wenn der Browser die erste Schriftart nicht unterstützt, versucht er die nächste. Zum Beispiel,
font-family="Verdana, cursive"
font-family="'Times New Roman', Georgia, Serif, monospace"
font Diese Eigenschaft legt alle Schrifteigenschaften in einer Deklaration fest. Die Eigenschaften , die eingestellt werden können , sind (in dieser Reihenfolge)
font-style font-variant font-weight font-size font-family
Zum Beispiel
"italic small-caps bold 12px arial, sans-serif",

3.3 HTML5-Dateivorlagen

Änderungen in HTML5

Beachten Sie, dass es einige neue Regeln für den Wechsel von HTML4 zu HTML5 gibt:

Überblick

Im Folgenden werden einige Vorlagen für HTML5-Dateien aufgelistet und erläutert, die Canvas-Elemente enthalten. Alle diese Beispiele erzeugen dieselbe Browseransicht, nämlich:

Der Unterschied liegt darunter in der Organisation des HTML- und JavaScript-Codes.

Erste Vorlage: sequentieller Code in einer Datei

Der Text mit einem Kreis und einem Quadrat wird beispielsweise von der folgenden HTML-Datei generiert example.html

<!DOCTYPE html>
<html>
  <head>
    <title> First Example </title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style type="text/css">
      canvas { border: solid 1pt blue; }      /* draws a blue frame around each <canvas>...</canvas> tag */
    </style>
  </head>
  <body>
    A red circle
    <canvas id="CanvasNo1" width=50 height=50> </canvas>
    <script>
      var canvas1 = document.getElementById('CanvasNo1');
      var context1 = canvas1.getContext('2d');
      context1.fillStyle = 'red';
      context1.beginPath();
      context1.arc (25,25,15,0,2*Math.PI);
      context1.fill();
    </script>
    and a green square
    <canvas id="CanvasNo2" width=50 height=50> </canvas>
    <script>
      var canvas2 = document.getElementById('CanvasNo2');
      var context2 = canvas2.getContext('2d');
      context2.fillStyle='green';
      context2.fillRect (10,10,30,30);
    </script>
    and nothing else.
  </body>    
</html>

In dieser Datei haben wir die Methode aus der Einführung verwendet (siehe Ein erstes Beispiel und Grundeinstellung ): Für jede Zeichenfläche platzieren wir ein <canvas>...</canvas>Tag mit einem separaten idAttribut, gefolgt von a<script>...</script> , in dem die Zeichenfläche weiterentwickelt wird.

Diese Methode reicht für einfache Bilder und kleine Dateien aus. Wenn die Bilder jedoch komplexer werden und externe Bild- oder Mediendateien betroffen sind, funktioniert dies nicht mehr und stattdessen werden leere Leinwandbilder angezeigt. Im Allgemeinen ist es daher kein guter Rat, Ihre Dateien so zu organisieren.

Übrigens , je nach Art des Codes gibt es unterschiedliche Syntaxregeln für Kommentare :

  • In HTML wird ein Kommentar ...in a eingefügt<!-- ... -->

  • In CSS wird ein Kommentar ...in a eingefügt/* ... */

  • In JavaScript ist ein Kommentar alles nach einem //in der angegebenen Zeile.
    Alternativ gibt es auch den Blockkommentar /* ... */wie in CSS.

Zweite Vorlage: separate Dateien

Wenn wir uns mit nicht trivialen Projekten befassen, ist es eine gute Gewohnheit, Stil von Inhalt zu unterscheiden und die verschiedenen Codes in separate Dateien zu platzieren. In unserem Fall sind dies drei:

Die Haupt-HTML-Datei enthält den Inhalt und definiert die Organisation der gesamten Seite. Die CSS-Datei legt die nicht standardmäßigen Stilfunktionen fest und wird über integriert

<link rel="stylesheet" type="text/css" href="example.css" />

Anweisung. Das JavaScript enthält schließlich die Zeichnungen für die beiden Canvas-Elemente und wird über integriert

<script src="example.js"> </script>

Diese Trennung der Komponenten wird normalerweise als besseres Design angesehen.

Ist dies jedoch Mode, funktioniert es nicht! Anstelle des vorherigen Bildes mit Kreis und Quadrat sehen wir dies

im Browser. Es wird nur der CSS-Rand der Canvas-Elemente angezeigt, ohne dass die von JavaScript generierten Bildelemente vorhanden sind.

Wenn der Browser beginnt, die HTML-Datei zu lesen, lädt er die CSS- und JavaScript-Datei, wie im <head>...</head>Abschnitt gefordert , und generiert dann die<body>...</body> Abschnitt mit den beiden Canvas-Elementen weiter. In dieser Ausführungsreihenfolge wird der JavaScript-Code ausgeführt, bevor die Canvas-Elemente vorhanden sind, und dies schlägt fehl. Die Leinwandbilder bleiben leer.

Es gibt verschiedene Strategien, um dieses Problem zu lösen:

Erste Lösung: Laden Sie externe jsDateien indefer Modus

In der vorherigen HTML-Datei example.htmlkönnen wir die Zeile ersetzen

<script src="example.js"> </script>

durch

<script src="example.js" defer> </script>

dh wir fügen das deferAttribut dem <script>Tag hinzu. Auf diese Weise wird die Ausführung der JavaScript-Datei example.jsverzögert, bis zuerst die gesamte HTML-Datei geladen wird. Mit dieser Änderung erhalten wir das beabsichtigte Browserbild

nochmal.

Zweite Lösung: onloadZum <body>Tag hinzufügen

Die Reihenfolge, in der verschiedene Codetypen ausgeführt werden, kann auch von JavaScript selbst beeinflusst werden. Alle Leinwandzeichnungen in JavaScript sind jetzt in einer Funktion mit dem Namen eingeschlossen draw(). Außerdem wird dem Body ein Ereignis-Listener hinzugefügt, der sicherstellt, dass er draw()nur ausgeführt wird onload, dh wenn der HTML-Code im Body zuerst vollständig geladen ist. Die drei Dateien sind jetzt gegeben durch

Dritte Lösung: window.onload

Als letzte Variation der vorherigen Vorlage können wir auch das gesamte JavaScript wieder aus der htmlDatei entfernen und den Ereignis-Listener zur jsDatei hinzufügen . Der zugrunde liegende Mechanismus ist immer noch der gleiche, aber die Codierung ist unterschiedlich. Dies beinhaltet die folgenden Änderungen der vorherigen Dateilisten:

Erhöhte Fehlererkennung

Die letzten Vorlagen stellen sehr häufig die allgemein empfohlenen Methoden dar, wie Canvas-Elemente in Webseiten integriert werden sollen. Jede dieser Methoden kann noch verfeinert werden, indem die Fehlererkennung erhöht wird. Sowohl Browser als auch JavaScript sind ursprünglich so konzipiert, dass sie in dem Sinne "elegant degradieren", dass sie versuchen, den Zusammenbruch von Dingen zu vermeiden, aber dazu neigen, so viel wie möglich zu verarbeiten. Der Browser, der <canvas>Tags nicht versteht, zeigt den Fallback (den Text ...im <canvas>...</canvas>) an, anstatt eine große Fehlermeldung auf dem gesamten Bildschirm zu drucken. Und wenn eine Variable in JavaScript nicht definiert ist, protestiert das Skript nicht im Browserfenster, sondern versucht, mit einem undefinedWert für die Variable fortzufahren .

Der Preis für dieses fehlerverzeihende Design ist die Schwierigkeit, den Ursprung von Problemen zu lokalisieren, sobald sie aufgetreten sind. Und je professioneller die Programmierung, desto strenger und weniger fehlerverzeihend die Programmabläufe und desto mehr Fehlererkennungen sind eingebaut.

Für den HTML-Teil bedeutet dies, dass normalerweise ein Fallback-Text wie z

<canvas> HERE SHOULD BE THE CANVAS PICTURE, BUT YOUR BROWSER IS UNABLE TO RENDER IT! <canvas>

Für den JavaScript-Teil umfasst dies die Modularisierung von Code in separate Teile und eine gründliche Überprüfung der Richtigkeit von Variablen und Objekten.

Zum Beispiel können wir die draw()Funktion für jedes vorkommende Canvas-Element in drawCanvas1()und aufteilen drawCanvas2(). Und anstatt zu schreiben, sagen Sie

function drawCanvas2() {
  var canvas = document.getElementById('CanvasNo2');
  var context = canvas.getContext('2d');
  context.fillStyle='green';
  context.fillRect (10,10,30,30);
};

Wir können solche Überprüfungen einbauen

function drawCanvas2() {
  var canvas = document.getElementById('CanvasNo2');
  if (canvas) {
    var context = canvas.getContext('2d');
    if (context) {
      context.fillStyle='green';
      context.fillRect (10,10,30,30);
    } else {
      throw Error ("The 2D context is undefined.");
    }
  } else {
    throw Error ("There is no HTML element with the id='CanvasNo2'.");
  };
};

Anstelle von ziemlich stillen Fehlermeldungen können wir auch Popup-Fenster alarmieren lassen: Ersetzen Sie einfach jedes

throw Error ("...");

durch

alert ("...");

3.4 Werkzeuge und Werkzeugbau

3.4.1 Wie schreibe ich eigene Funktionen wie strokeCircle()undfillCircle()

In JavaScript ist es recht einfach, das Toolset für den Canvas- und 2D-Kontext nach Belieben durch Schreiben von Funktionen zu erweitern. Angenommen, wir möchten Funktionen schreiben, die Kreise erstellen, die dem Weg fillRect()und sehr ähnlich sindstrokeRect() zum Erstellen von Rechtecken funktionieren.

Diese Funktionen sollten zwei Zahlenargumente xund yfür die Position des Kreismittelpunkts und habenr für den Radius haben.

Erste Version

Die erste Version unserer Implementierung ist diese

function fillCircle (contextObj,x,y,r) {
  contextObj.beginPath();
  contextObj.arc (x,y,r,0,2*Math.PI);
  contextObj.fill();
};

Dies bedeutet, dass die Syntax eines Funktionsaufrufs lautet

`fillCircle (contextObj, x, y, r)`

Wo contextObjist ein CanvasRenderingContext2DObjekt? Also, um ein Bild mit einer grünen Kugel wie dieser zu erzeugen

Sie müssten einen Code wie diesen schreiben

<canvas id="FillCircleTest1" width=60 height=60> </canvas>
<script>
  var context = document.getElementById('FillCircleTest1').getContext('2d');
  context.fillStyle = 'green';
  fillCircle(context,30,30,20);
</script>
Zweite Version

Alternativ können wir die Funktionssyntax in ändern

`fillCircle (canvasId, x, y, r)`

Wo canvasIdist der Wert des idAttributs im entsprechenden <canvas>Tag? Dies wird implementiert von

function fillCircle (canvasId,x,y,r) {
  var context = document.getElementById (canvasId).getContext('2d');
  context.beginPath();
  context.arc (x,y,r,0,2*Math.PI);
  context.fill();
};

Auf diese Weise könnten wir einfach schreiben

<canvas id="FillCircleTest2" width=60 height=60> </canvas>
<script>
  fillCircle('FillCircleTest2',30,30,20);
</script>

um dieses Bild zu erzeugen

Da diese Version jedoch den Kontext ausschließt und der Kontext das Objekt ist, das Sie benötigen, wenn Sie beispielsweise die Farbe ändern möchten.

Dritte Version

Wenn wir unsere Funktionen nach dem objektorientierten Design des Kontexts erstellen möchten, möchten wir den Kontext lieber nicht als Funktionsargument haben (wie in der ersten Version), sondern fillCircle()sollten eine Methode für den 2d-Kontext sein, damit wir können es so nennen

context.fillCircle (x, y, r)

Dafür müssten wir das Methodenrepertoire des CanvasRenderingContext2DObjekts erweitern. Tatsächlich ist dies in JavaScript möglich, indem das Prototypendesign verwendet wird. Die Implementierung wäre also so

CanvasRenderingContext2D.prototype.fillCircle = function (x,y,r) {
  this.beginPath();
  this.arc (x,y,r,0,2*Math.PI);
  this.fill();
}

Jetzt erstellen wir dieses Leinwandbild

indem Sie diesen Code schreiben

<canvas id="FillCircleTest3" width=60 height=60> </canvas>
<script>
  var context = document.getElementById('FillCircleTest3').getContext('2d');
  context.fillStyle = 'purple';
  context.fillCircle (30,30,20);
</script>

Die strokeCircle()Implementierung auf diese Weise ist dann offensichtlich

CanvasRenderingContext2D.prototype.strokeCircle = function (x,y,r) {
  this.beginPath();
  this.arc (x,y,r,0,2*Math.PI);
  this.stroke();
}

Diese dritte prototypbasierte Implementierung kommt dem ursprünglichen Stil wahrscheinlich am nächsten. 30

3.4.2 addGrid(delta,color,font)

CanvasRenderingContext2D.addGrid (delta, color, font)

Zeichnet ein Koordinatengitter auf der angegebenen Leinwand. Es hat drei Parameter:

  • deltaist der Abstand der Linien im Raster, angegeben als Anzahl der Pixel, mit einem Standardwert von 25.
  • colorist eine CSS-Farbzeichenfolge , die die Farbe des Rasters und die Zahlenkoordinaten festlegt. Die Standardfarbe ist 'blue'.
  • fontist eine CSS-Schriftzeichenfolge zum Bestimmen der Schriftart der Zahlenkoordinaten. Der Standardwert für die Schriftart ist '8px sans-serif'.

Jeder der Parameter ist optional, Sie können anrufen addGrid(), addGrid(delta), addGrid(delta,color)oder addGrid(delta,color,font).

Beispiel: Auf einer Leinwand mit Standardgröße ( 300x 150)

Implementierung von addGrid()

Wenn Sie die addGrid()Methode in Ihren eigenen Skripten verwenden möchten , kopieren Sie die folgende Implementierung der Funktion in Ihre eigene Datei.

CanvasRenderingContext2D.prototype.addGrid = function (delta, color, fontParams) {
  // define the default values for the optional arguments
  if (! arguments[0]) { delta = 25; }
  if (! arguments[1]) { color = 'blue'; }
  if (! arguments[2]) { fontParams = '8px sans-serif'; }
  // extend the canvas width and height by delta
  var oldWidth = this.canvas.width;
  var oldHeight = this.canvas.height;      
  this.canvas.width = oldWidth + delta;
  this.canvas.height = oldHeight + delta;        
  // draw the vertical and horizontal lines
  this.lineWidth = 0.1;
  this.strokeStyle = color;
  this.font = fontParams;
  this.beginPath();
  for (var i = 0; i * delta < oldWidth; i ++) {
    this.moveTo (i * delta, 0);
    this.lineTo (i * delta, oldHeight);
  }
  for (var j = 0; j * delta < oldHeight; j ++) {
    this.moveTo (0, j * delta);
    this.lineTo (oldWidth, j * delta);
  }      
  this.closePath();
  this.stroke();
  // draw a thicker line, which is the border of the original canvas
  this.lineWidth = 0.5;
  this.beginPath();
  this.moveTo(0,0);
  this.lineTo(oldWidth,0);
  this.lineTo(oldWidth,oldHeight);
  this.lineTo(0,oldHeight);
  this.lineTo(0,0);
  this.closePath();
  this.stroke();
  // set the text parameters and write the number values to the vertical and horizontal lines
  this.font = fontParams
  this.lineWidth = 0.3;
  // 1. writing the numbers to the x axis
  var textY = oldHeight + Math.floor(delta/2); // y-coordinate for the number strings
  for (var i = 0; i * delta <= oldWidth; i ++) {
    this.strokeText (i * delta, i * delta, textY);        
  }
  // 2. writing the numbers to the y axis
  var textX = oldWidth + 5; // x-coordinate for the number strings
  for (var j = 0; j * delta <= oldHeight; j ++) {
    this.strokeText (j * delta, textX, j * delta);
  }
};

  1. [HOME] /handbuch/index.php?ordner=handbuch&name=CanvasHandbook.php?

  2. [FORUM] http://www-bucephalus-org.blogspot.nl/2013/09/the-html5-canvas-handbook.html?

  3. Diese endgültige HTML-Datei CanvasHandbook.html wird aus den folgenden Quelldateien generiert:

    1. CanvasHandbook.markdown enthält den Quelltext für das HTML-Dokument. Die Konvertierung in HTML erfolgt mit Pandoc .

    2. CanvasHandbook.js enthält die Werkzeugfunktionen (z. B. addGrid()) und die Skripte für die Canvas-Elemente. Dieser Code wird gemäß der im Anhang beschriebenen Methode in das Hauptdokument integriert .
      Ich habe auch den CodeDown- Dokumentgenerator verwendet, um die jsDatei in eine besser lesbare Dokumentdatei CanvasHandbook.js.html zu konvertieren , die selbst aus einer Markdown-Zwischendatei CanvasHandbook.js.markdown generiert wurde .

    3. Zwei CSS-Dateien: das Standard- Stylesheet CodeDown.css und CodeDownPrint.css zum Drucken der Datei mit einer kleineren Schriftart.

    4. Bilddateien: Horse.jpg , Grayhorse.jpg , Baselines.png , TemplateCircleAndSquare.png und TemplateCircleAndSquare2.png . Alle anderen Bilder im Browser werden durch <canvas>Tags und JavaScript generiert .

    ?
  4. [W3C] http://www.w3.org/TR/2009/WD-html5-20090825/the-canvas-element.html?

  5. [WHATWG] http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element?

  6. [W3C / 2D] http://dev.w3.org/html5/2dcontext?

  7. Wenn Sie die Meldung "Hallo" nicht sehen oder "GO AND UPDATE YOUR BROWSER BEVOR SIE LESEN!" Lesen. Stattdessen sollten Sie diesen Rat wirklich befolgen. Andernfalls wird keines der Bilder in diesem Handbuch richtig angezeigt und der gesamte Text macht keinen Sinn. ?

  8. Was Sie im <script>...</script>Tag sehen, ist nicht HTML, sondern JavaScript-Code, und das hat eine andere Syntax. Ein wichtiges Merkmal ist der Kommentar , eine Information, die in den Programmcode eingefügt wird und nur als Hilfe zum Verständnis und zur Pflege des Codes gedacht ist. Ein Kommentar dient nur dem menschlichen Leser des Codes und wird vom Computerprogramm, in diesem Fall der JavaScript-Engine des Browsers, ignoriert.
    Der gesamte Text in einer Zeile nach einem doppelten Schrägstrich " //" ist ein Kommentar in JavaScript. Im angegebenen Beispiel ist " // identify the canvas element" ein Kommentar und erklärt, was der vom " //" übrig gebliebene Code bewirkt. Wir werden Kommentare in all unseren JavaScript-Codebeispielen stark nutzen. ?

  9. Sie kennen wahrscheinlich das DOM (Document Object Model). Jedes Webdokument wird zunächst als HTML-Datei angegeben. Wenn dieser HTML-Code geladen wird, übersetzt der Browser das Dokument in ein hierarchisches JavaScript-Baumobjekt. Jedes Element des HTML-Codes wird in diesem Baum als Knotenobjekt dargestellt. Auf die Wurzel dieses Baums wird über das vordefinierte documentSchlüsselwort zugegriffen . Auf jeden Elementknoten in diesem Baum, der einem Tag mit einem entspricht, idwird dann durch einen Aufruf von zugegriffen document.getElementById('...'). Alternativ können wir auch das Array aller HTMLCanvasElementObjekte in einem Dokument durch Aufrufen abrufen document.getElementsByTagName('canvas'). Dies werden wir in unserem Text hier jedoch nicht verwenden. ?

  10. In JavaScript wird eine Zeichenfolge entweder in einfache '...'oder doppelte Anführungszeichen eingeschlossen "...". Zum Beispiel 'Hello world!'und "Hello world!"beide bezeichnen dieselbe Zeichenfolge. Es gibt subtile Unterschiede zwischen diesen beiden Versionen, aber im Moment können wir diese Details vernachlässigen. So document.getElementById('Example1')und document.getElementById("Example1")haben die gleiche Wirkung und Sie können zwischen diesen beiden Versionen frei wählen. ?

  11. Beachten Sie, dass die Richtung der y-Achse in kartesischen Koordinatensystemen wirklich der Standardrichtung entgegengesetzt ist ! ?

  12. Tatsächlich ist der gezeichnete rote Punkt nicht nur 1 Pixel groß, sondern hat einen Radius von 4 Pixel. Andernfalls ist es möglicherweise zu schwer zu erkennen. ?

  13. Es ist jedoch möglich, das gesamte Koordinatensystem auf der Leinwand so zu verschieben, dass z. B. der Ursprung (0,0)nicht mehr die linke obere Ecke ist, sondern sich in die Mitte bewegt und Punkte wie (-60,50)jetzt Teil des Bildes werden. Dies kann mit Transformationen wie translate(x,y). ?

  14. Die sechs Linien, die die drei farbigen Rechtecke zeichnen, werden gemäß unserer vorherigen Vorlage in diesen Code eingeschlossen:

    <canvas id="GermanFlag" width=200 height=120> </canvas>
    <script>
      // get the canvas object and its context
      var canvas = document.getElementById('GermanFlag');
      var context = canvas.getContext('2d');
      // add the grid with 40 pixels for each step to the next line (see the appendix on this addGrid() method
      context.addGrid(40);            // THIS IS NOT A STANDARD METHOD!!!
      // now draw the colored rectangles
      // now draw the colored rectangles
      context.fillStyle = '#000000';
      context.fillRect(0, 0,200,40);
      context.fillStyle = '#FF0000';
      context.fillRect(0,40,200,40);
      context.fillStyle = '#FFCC00';
      context.fillRect(0,80,200,40);
    </script>
    ?
  15. Diese Idee der anklickbaren Zeichenfläche wurde in html5doctor.com vorgeschlagen . ?

  16. Die transform()Methode ist jedoch nicht in der Lage, eine (topologische) Transformation im allgemeinen geometrischen Sinne durchzuführen, sondern nur eine sogenannte affine Transformation . Beispielsweise kann keine Fischaugenperspektive erzeugt werden , da bei affinen Transformationen gerade und parallele Linien gerade bzw. parallel bleiben müssen. Es ist jedoch möglich, topologische Transformationen unter Verwendung der Pixelmanipulationen zu implementieren , aber das ist ein anderer Ansatz. ?

  17. Mit der im Anhang beschriebenen Methode können wir shear()dem CanvasRenderingContext2DObjekt wie folgt eine neue Methode hinzufügen :

    CanvasRenderingContext2D.prototype.shear = function (x,y) {
      this.transform (1, y, x, 1, 0, 0);
    };

    Ich habe die Idee und Definition der shear()Methode von David Flanagan , Canvas Pocket Reference, übernommen . ?

  18. Es ist hier nicht wichtig, die genaue Datenstruktur für dieses Sechstel anzugeben (a,b,c,d,e,f). Es kann als Array angegeben werden [a,b,c,d,e,f]. In [WHATWG] wurde dies als SVGMatrixObjekt gespeichert . Für die Berechnung von Transformationszusammensetzungen ist es zweckmäßig, sie als 3x2- Matrix des Formulars zu speichern
    a c e
    b d f
    0 0 1

    denn die Zusammensetzung ist dann nichts als eine Matrixmultiplikation . ?

  19. Die Definition der Zusammensetzung ?lautet wie folgt:
    Gegeben und dann(a1,b1,c1,d1,e1,f1)(a2,b2,c2,d2,e2,f2)
    (a1,b1,c1,d1,e1,f1)?(a2,b2,c2,d2,e2,f2) = (a3,b3,c3,d3,e3,f3)
    wo
    a3 = (a2 * a1) + (c2 * b1)
    b3 = (b2 * a1) + (d2 * b1)
    c3 = (a2 * c1) + (c2 * d1)
    d3 = (b2 * c1) + (d2 * d1)
    e3 = (a2 * e1) + (c2 * f1) + e2
    b3 = (b2 * e1) + (d2 * f1) + f2
    Wenn die Parameter als 3 × 3-Matrizen gespeichert werden, ist die Zusammensetzung nichts anderes als eine Matrixmultiplikation, nämlich
    a2 c2 e2
    b2 d2 f2
    0 0 1
    ?
    a1 c1 e1
    b1 d1 f1
    0 0 1
    =
    a3 c3 e3
    b3 d3 f3
    0 0 1

    Beachten Sie, dass dies bedeutet, dass zuerst und dann ausgeführt wird. Bei der Matrixmultiplikation ist diese Reihenfolge umgekehrt: Die Matrix von wird mit der Matrix von multipliziert , nicht umgekehrt. (Zusammensetzung und Matrixmultiplikation sind nicht kommutativ . Sie sind jedoch assoziativ , dh = .) Wir können dies auch als JavaScript-Funktion implementieren: Geben Sie zwei Arrays an und geben Sie dann deren Zusammensetzung zurück .?1??2?1?2?2?1(?1??2)??3?1?(?2??3)

    arr1=[a1,b1,c1,d1,e1,f1]arr2=[a2,b2,c2,d2,e2,f2]composeTransform(arr1,arr2)[a3,b3,c3,d3,e3,f3]

    function composeTransform (arr1, arr2) {
      if (Array.isArray (arr1) && Array.isArray (arr2)) {
        if (arr1.length === 6 && arr2.length === 6) {
          // components of arr1
          var a1 = arr1[0]; var b1 = arr1[1]; var c1 = arr1[2]; var d1 = arr1[3]; var e1 = arr1[4]; var f1 = arr1[5];
          // components of arr2
          var a2 = arr2[0]; var b2 = arr2[1]; var c2 = arr2[2]; var d2 = arr2[3]; var e2 = arr2[4]; var f2 = arr2[5];
          // components of the resulting array
          var a3 = (a2 * a1) + (c2 * b1);
          var b3 = (b2 * a1) + (d2 * b1);
          var c3 = (a2 * c1) + (c2 * d1);
          var d3 = (b2 * c1) + (d2 * d1);
          var e3 = (a2 * e1) + (c2 * f1) + e2;
          var f3 = (b2 * e1) + (d2 * f1) + f2;
          return [a3, b3, c3, d3, e3, f3];
        } else {
          throw Error ("The two array arguments of composeTransform(arr1,arr2) must both have six components each.");
        }
      } else {
        throw Error ("The two arguments of composeTransform(arr1,arr2) must both be arrays.");
      }
    };

    Falls die Argumente arr1und arr2nicht beide Arrays der Länge 6 sind, wird eine Fehlermeldung ausgegeben. ?

  20. Wie bereits erwähnt, waren in dieser Norm die Parameter (a,b,c,d,e,f)von currentTransformein SVGMatrixObjekt. ?

  21. Die Definition der Zusammensetzung ?lautet wie folgt:
    Gegeben und dann(a1,b1,c1,d1,e1,f1)(a2,b2,c2,d2,e2,f2)
    (a1,b1,c1,d1,e1,f1)?(a2,b2,c2,d2,e2,f2) = (a3,b3,c3,d3,e3,f3)
    wo
    a3 = (a2 * a1) + (c2 * b1)
    b3 = (b2 * a1) + (d2 * b1)
    c3 = (a2 * c1) + (c2 * d1)
    d3 = (b2 * c1) + (d2 * d1)
    e3 = (a2 * e1) + (c2 * f1) + e2
    b3 = (b2 * e1) + (d2 * f1) + f2
    Wenn die Parameter als 3 × 3-Matrizen gespeichert werden, ist die Zusammensetzung nichts anderes als eine Matrixmultiplikation, nämlich
    a2 c2 e2
    b2 d2 f2
    0 0 1
    ?
    a1 c1 e1
    b1 d1 f1
    0 0 1
    =
    a3 c3 e3
    b3 d3 f3
    0 0 1

    Beachten Sie, dass dies bedeutet, dass zuerst und dann ausgeführt wird. Bei der Matrixmultiplikation ist diese Reihenfolge umgekehrt: Die Matrix von wird mit der Matrix von multipliziert , nicht umgekehrt. (Zusammensetzung und Matrixmultiplikation sind nicht kommutativ . Sie sind jedoch assoziativ , dh = .) Wir können dies auch als JavaScript-Funktion implementieren: Geben Sie zwei Arrays an und geben Sie dann deren Zusammensetzung zurück .?1??2?1?2?2?1(?1??2)??3?1?(?2??3)

    arr1=[a1,b1,c1,d1,e1,f1]arr2=[a2,b2,c2,d2,e2,f2]composeTransform(arr1,arr2)[a3,b3,c3,d3,e3,f3]

    function composeTransform (arr1, arr2) {
      if (Array.isArray (arr1) && Array.isArray (arr2)) {
        if (arr1.length === 6 && arr2.length === 6) {
          // components of arr1
          var a1 = arr1[0]; var b1 = arr1[1]; var c1 = arr1[2]; var d1 = arr1[3]; var e1 = arr1[4]; var f1 = arr1[5];
          // components of arr2
          var a2 = arr2[0]; var b2 = arr2[1]; var c2 = arr2[2]; var d2 = arr2[3]; var e2 = arr2[4]; var f2 = arr2[5];
          // components of the resulting array
          var a3 = (a2 * a1) + (c2 * b1);
          var b3 = (b2 * a1) + (d2 * b1);
          var c3 = (a2 * c1) + (c2 * d1);
          var d3 = (b2 * c1) + (d2 * d1);
          var e3 = (a2 * e1) + (c2 * f1) + e2;
          var f3 = (b2 * e1) + (d2 * f1) + f2;
          return [a3, b3, c3, d3, e3, f3];
        } else {
          throw Error ("The two array arguments of composeTransform(arr1,arr2) must both have six components each.");
        }
      } else {
        throw Error ("The two arguments of composeTransform(arr1,arr2) must both be arrays.");
      }
    };

    Falls die Argumente arr1und arr2nicht beide Arrays der Länge 6 sind, wird eine Fehlermeldung ausgegeben.

  22. Auch hier können wir die beiden Transformationen und Codezeilen kombinieren

    context.scale(-1,1);          // same as context.transform(-1,0,0,1,0,0);
    context.translate(-300,0);    // same as context.transform(1,0,0,1,-300,0);
    durch Ausführen der Komposition zu einer einzigen
    (-1,0,0,1,0,0) ? (1,0,0,1,-300,0) = (-1,0,0,1,-300,0)
    ?
  23. Natürlich hätten wir den Pfad auch selbst schließen können, indem wir die closePath()Zeile im Code durch die alternative Zeile ersetzt hätten

      context.lineTo(150,25);        // line from left bottom back to the initial top point

    Das resultierende Bild ist jedoch nicht immer genau das gleiche, da Anfang und Ende der Zeile möglicherweise nicht richtig zusammengeführt werden. ?

  24. Bevor wir den stroke()Anruf hinzugefügt haben , haben wir lineWidthauf 2.0und strokeStyleauf gesetzt 'red'. ?

  25. Tatsächlich ist das Ergebnis für closePath()und a nicht genau dasselbe lineTo(x0,y0)(wobei (x0,y0)dies der Anfangspunkt des Pfades ist). Bei der letzteren Methode treffen sich zwei Zeilenenden, und ihre Endungen werden wie durch definiert definiert lineCap. Ein ordnungsgemäßer closePath()Aufruf hingegen verbindet die beiden durch die lineJoinEigenschaft definierten Endungen . ?

  26. Die [WHATWG] -Spezifikation hatte eine separate ellipse()Methode, die jedoch im [W3C] -Standard verschwunden ist . Die Form, die wir hier mit vier quadraticCurveTo()Aufrufen entwickeln, ist keine Ellipse im engeren mathematischen Sinne. In vielen praktischen Situationen reicht dies jedoch wahrscheinlich aus. ?

  27. Dieses Beispiel wurde von dem Beispiel im Canvas-Tutorial des Mozilla Development Network inspiriert . ?

  28. Das Einfügen von Bildern mit drawImage()wird unter Zeichnen von Bildern ? erläutert

  29. Die Standardliteratur verwendet die Variablen dx, dy, dw, dh, sx, sy, sw, sh. Es kann hilfreich sein, wenn Sie wie gewohnt " d" als Ziel , " s" als Quelle und " x", " y", " w" und " h" als Punktkoordinaten, Breite und Höhe lesen . ?

  30. Angenommen, Sie mögen die objektorientierte, prototypbasierte oder prozedurale Programmierung nicht und bevorzugen stattdessen einen rein funktionalen Stil. In diesem Fall würden wir uns eine Funktion fillCircle()vorstellen, die ein Kreisobjekt zurückgibt, wenn es aufgerufen wird. Die [WHATWG] hatte ein PathObjekt, das Formen im Allgemeinen abdeckte, sodass fillCircle()ein PathObjekt zurückgegeben werden konnte. Aber in der "Evolution" zum [W3] -Standard wurden einige Konzepte, einschließlich Path, weggeschnitten, und die von mir getesteten Browser verbergen den Zugriff auf all dies. ?