Erste Schritte in WebGL, Teil 2 Das Canvas-Element für unseren ersten Shader

Im vorigen Artikel haben wir unsere ersten Vertex- und Fragment-Shader geschrieben. Nachdem Sie den GPU-seitigen Code geschrieben haben, ist es Zeit zu lernen, wie der CPU-seitige geschrieben wird. In diesem und im nächsten Tutorial zeige ich Ihnen, wie Sie Shader in Ihre WebGL-Anwendung integrieren. Wir fangen bei Null an und verwenden nur JavaScript und keine Bibliotheken von Drittanbietern. In diesem Teil behandeln wir den Canvas-spezifischen Code. Im nächsten werden wir den WebGL-spezifischen Inhalt behandeln.

Beachten Sie, dass diese Artikel:

  • Nehmen Sie an, Sie kennen sich mit GLSL-Shadern aus. Wenn nicht, lesen Sie bitte den ersten Artikel.
  • sind nicht dazu gedacht, Ihnen HTML, CSS oder JavaScript beizubringen. Ich werde versuchen, die kniffligen Konzepte zu erklären, wenn wir ihnen begegnen, aber Sie müssen im Web nach weiteren Informationen suchen. Das MDN (Mozilla Developer Network) ist dafür ein hervorragender Ort.

Lass uns schon anfangen!

Was ist WebGL??

WebGL 1.0 ist eine einfache 3D-Grafik-API für das Web, die über das HTML5-Canvas-Element bereitgestellt wird. Es ist eine Shader-basierte API, die der OpenGL ES 2.0-API sehr ähnlich ist. WebGL 2.0 ist dasselbe, basiert jedoch stattdessen auf OpenGL ES 3.0. WebGL 2.0 ist nicht vollständig abwärtskompatibel mit WebGL 1.0, aber die meisten fehlerfreien WebGL 1.0-Anwendungen, die keine Erweiterungen verwenden, sollten unter WebGL 2.0 problemlos funktionieren.

Zum Zeitpunkt der Erstellung dieses Artikels sind WebGL 2.0-Implementierungen noch in den wenigen Browsern experimentell, die ihn implementieren. Sie sind auch standardmäßig nicht aktiviert. Daher ist der Code, den wir in dieser Serie schreiben werden, auf WebGL 1.0 ausgerichtet.

Sehen Sie sich das folgende Beispiel an (denken Sie daran, die Registerkarten zu wechseln, und werfen Sie auch einen Blick auf den Code):

Dies ist der Code, den wir schreiben werden. Ja, es erfordert tatsächlich etwas mehr als hundert Zeilen JavaScript, um etwas so einfaches zu implementieren. Aber machen Sie sich keine Sorgen, wir nehmen uns Zeit, um sie zu erklären, damit sie am Ende alle einen Sinn ergeben. In diesem Lernprogramm werden wir den Canvas-bezogenen Code behandeln und im nächsten mit dem WebGL-spezifischen Code fortfahren.

Die Leinwand

Zuerst müssen wir eine Leinwand erstellen, auf der wir unser gerendertes Material zeigen. 

Dieses niedliche kleine Quadrat ist unsere Leinwand! Wechseln Sie zu HTML anschauen und mal sehen, wie wir es gemacht haben.

Damit teilen Sie dem Browser mit, dass unsere Seite auf mobilen Geräten nicht zoombar sein soll.

Und das ist unser Leinwandelement. Wenn wir unserer Leinwand keine Bemaßungen zugewiesen hätten, wäre der Standardwert 300 * 150px (CSS-Pixel). Wechseln Sie jetzt zu CSS Um zu sehen, wie wir es gestaltet haben.

Segeltuch … 

Dies ist ein CSS-Selektor. Dies bedeutet, dass die folgenden Regeln auf alle Leinwandelemente in unserem Dokument angewendet werden. 

Hintergrund: # 0f0;

Schließlich muss die Regel auf die Elemente der Leinwand angewendet werden. Der Hintergrund ist auf hellgrün eingestellt (# 0f0).

Hinweis: Im obigen Editor wird der CSS-Text automatisch an das Dokument angehängt. Wenn Sie Ihre eigenen Dateien erstellen, müssen Sie die CSS-Datei in Ihrer HTML-Datei folgendermaßen verknüpfen:

Legen Sie es vorzugsweise in die Kopf Etikett.

Nun, da die Leinwand fertig ist, ist es Zeit, etwas zu zeichnen! Während die Leinwand dort oben schön und schön aussieht, haben wir leider noch einen langen Weg vor uns, bevor wir mit WebGL etwas zeichnen können. Also Schrott WebGL! In diesem Lernprogramm führen wir eine einfache 2D-Zeichnung aus, um einige Konzepte zu erläutern, bevor Sie zu WebGL wechseln. Unsere Zeichnung sei eine Diagonale.

Rendering-Kontext

Der HTML-Code stimmt mit dem letzten Beispiel überein, mit Ausnahme dieser Zeile:   

in dem wir ein gegeben haben Ich würde auf das Canvas-Element, damit wir es leicht in JavaScript abrufen können. Das CSS ist genau das gleiche und eine neue JavaScript-Registerkarte wurde hinzugefügt, um die Zeichnung auszuführen.

Wechseln Sie zu JS Tab,

window.addEventListener ('load', function () …);

Im obigen Beispiel soll der von uns geschriebene JavaScript-Code an den Dokumentkopf angehängt werden, dh er wird ausgeführt, bevor die Seite vollständig geladen ist. In diesem Fall können wir jedoch nicht auf die noch zu erstellende Leinwand zeichnen. Deshalb verschieben wir den Code erst nach dem Laden der Seite. Dazu verwenden wir window.addEventListener, spezifizieren Belastung Als Ereignis möchten wir uns anhören und unseren Code als Funktion, die ausgeführt wird, wenn das Ereignis ausgelöst wird.

Umzug:

var canvas = document.getElementById ("Leinwand");

Erinnern Sie sich an die ID, die wir zuvor im HTML der Leinwand zugewiesen haben? Hier wird es nützlich. In der obigen Zeile rufen wir das Canvas-Element aus dem Dokument ab, wobei seine ID als Referenz verwendet wird. Ab jetzt werden die Dinge interessanter,

context = canvas.getContext ('2d');

Um auf der Leinwand zeichnen zu können, müssen wir zunächst einen Zeichnungskontext erstellen. Ein Kontext in diesem Sinne ist ein Hilfsobjekt, das die erforderliche Zeichnungs-API verfügbar macht und mit dem Zeichenbereichselement verknüpft. Dies bedeutet, dass jede nachfolgende Verwendung der API, die diesen Kontext verwendet, für das betreffende Canvas-Objekt ausgeführt wird.

In diesem speziellen Fall haben wir a 2d Zeichnungskontext (CanvasRenderingContext2D), wodurch wir beliebige 2D-Zeichnungsfunktionen verwenden können. Wir hätten eine beantragen können webgl, ein webgl2 oder ein Bitmaprenderer stattdessen Kontexte, von denen jeder einen anderen Satz von Funktionen enthüllt hätte.

Der Hintergrundmodus einer Leinwand ist immer auf festgelegt keiner anfänglich. Dann durch anrufen getContext, Sein Modus ändert sich permanent. Egal wie oft Sie anrufen getContext Auf einer Leinwand ändert sich der Modus nach dem Festlegen nicht. Berufung getContext Für dieselbe API wird das gleiche Kontextobjekt zurückgegeben, das bei der ersten Verwendung zurückgegeben wird. Berufung getContext für eine andere API wird zurückgegeben Null.

Leider kann es schief gehen. In bestimmten Fällen, getContext kann keinen Kontext erstellen und löst stattdessen eine Ausnahme aus. Während dies heutzutage ziemlich selten ist, ist es mit möglich 2d Kontexte. Anstatt abstürzen, wenn dies passiert, haben wir unseren Code in a eingekapselt Try-Catch Block:

try context = canvas.getContext ('2d');  catch (exception) alert ("Umm ... Entschuldigung, keine 2d-Kontexte für Sie!" + exception.message); Rückkehr ; 

Auf diese Weise können wir, wenn eine Ausnahme ausgelöst wird, diese abfangen, eine Fehlermeldung anzeigen und dann behutsam unseren Kopf gegen die Wand schlagen. Oder zeigen Sie möglicherweise ein statisches Bild einer diagonalen Linie an. Wir könnten das zwar tun, aber es widerspricht dem Ziel dieses Tutorials!

Vorausgesetzt, wir haben erfolgreich einen Kontext erworben, bleibt nur noch die Linie zu zeichnen:

context.beginPath ();

Das 2d context merkt sich den zuletzt erstellten Pfad. Das Zeichnen eines Pfads löscht ihn nicht automatisch aus dem Speicher des Kontexts. beginPath weist den Kontext an, alle vorherigen Pfade zu vergessen und neu zu beginnen. Ja, in diesem Fall hätten wir diese Zeile ganz weglassen können, und es hätte einwandfrei funktioniert, da es keine vorherigen Pfade gab.

context.moveTo (0, 0);

Ein Pfad kann aus mehreren Unterpfaden bestehen. ziehen nach Startet einen neuen Unterpfad an den erforderlichen Koordinaten.

context.lineTo (30, 30);

Erzeugt ein Liniensegment vom letzten Punkt des Unterpfads bis (30, 30). Dies bedeutet eine diagonale Linie von der oberen linken Ecke der Leinwand (0, 0) bis zur rechten unteren Ecke (30, 30)..

context.stroke ();

Einen Pfad zu schaffen ist eine Sache; Zeichnen ist ein anderes. Schlaganfall weist den Kontext an, alle Unterpfade in seinem Speicher zu zeichnen.

beginPath, ziehen nach, lineTo, und Schlaganfall sind nur verfügbar, weil wir a 2d Kontext. Wenn wir zum Beispiel einen beantragten webgl In diesem Kontext wären diese Funktionen nicht verfügbar gewesen.

Hinweis: Im obigen Editor wird der JavaScript-Code automatisch an das Dokument angehängt. Wenn Sie Ihre eigenen Dateien erstellen, müssen Sie auf die JavaScript-Datei in Ihrer HTML-Datei folgendermaßen verweisen:

Sie sollten es in die Kopf Etikett.

Damit ist unser Line Drawing-Tutorial abgeschlossen! Aber irgendwie bin ich mit dieser winzigen Leinwand nicht zufrieden. Wir können mehr als das tun!

Canvas-Größe

Wir werden unserem CSS einige Regeln hinzufügen, damit die Leinwand die gesamte Seite ausfüllt. Der neue CSS-Code wird folgendermaßen aussehen:

html, Körper Höhe: 100%;  body margin: 0;  Zeichenfläche Anzeige: Block; Breite: 100%; Höhe: 100%; Hintergrund: # 888; 

Nehmen wir es auseinander:

html, Körper Höhe: 100%; 

Das html und Karosserie Elemente werden wie Blockelemente behandelt. Sie verbrauchen die gesamte verfügbare Breite. Sie dehnen sich jedoch gerade so weit vertikal aus, dass sie ihren Inhalt einwickeln. Mit anderen Worten, ihre Höhe hängt von der Höhe der Kinder ab. Wenn Sie die Höhe einer ihrer Kinder auf einen Prozentsatz ihrer Höhe einstellen, wird eine Abhängigkeitsschleife ausgelöst. Wenn wir ihren Höhen also nicht explizit Werte zuweisen, können wir die Kinderhöhen nicht relativ zu ihnen festlegen.

Da die Leinwand die gesamte Seite ausfüllen soll (die Höhe auf 100% des übergeordneten Elements setzen), setzen wir ihre Höhe auf 100% (der Seitenhöhe)..

body margin: 0; 

Browser verfügen über grundlegende Stylesheets, die jedem von ihnen gerenderten Dokument einen Standardstil zuweisen. Es heißt das Benutzer-Agent-Stylesheets. Die Stile in diesen Tabellen hängen von dem jeweiligen Browser ab. Manchmal können sie sogar vom Benutzer eingestellt werden.

Das Karosserie Das Element hat normalerweise einen Standardrand in den Stylesheets des Benutzeragenten. Wir möchten, dass die Leinwand die gesamte Seite ausfüllt, so dass wir die Ränder auf einstellen 0.

Leinwand Anzeige: Block;

Im Gegensatz zu Blockelementen sind Inline-Elemente Elemente, die wie Text in einer regulären Zeile behandelt werden können. Sie können Elemente vor oder nach ihnen in derselben Zeile haben und unter ihnen einen leeren Bereich haben, dessen Größe von der verwendeten Schriftart und -größe abhängt. Wir möchten keinen leeren Bereich unter unserer Leinwand, also setzen wir den Anzeigemodus einfach auf Block.

Breite: 100%; Höhe: 100%;

Wie geplant setzen wir die Leinwandmaße auf 100% der Seitenbreite und -höhe.

Hintergrund: # 888;

Wir haben das schon früher erklärt, nicht wahr?!

Seht das Ergebnis unserer Veränderungen…

Nein, wir haben nichts falsch gemacht! Das ist völlig normales Verhalten. Erinnern Sie sich an die Maße, die wir der Leinwand in der HTML Etikett?

Jetzt haben wir der Leinwand andere Dimensionen im CSS gegeben:

Leinwand … Breite: 100%; Höhe: 100%;… 

Es stellt sich heraus, dass die von uns im HTML-Tag festgelegten Dimensionen das steuern intrinsische Abmessungen der Leinwand. Die Leinwand ist mehr oder weniger ein Bitmap-Container. Die Abmessungen der Bitmap sind unabhängig davon, wie die Leinwand an ihrer endgültigen Position und den endgültigen Abmessungen auf der Seite angezeigt wird. Was diese definiert, sind die extrinsische Abmessungen, diejenigen, die wir im CSS festlegen.

Wie wir sehen können, wurde unsere kleine 30 * 30-Bitmap so gedehnt, dass sie die gesamte Leinwand ausfüllt. Dies wird vom CSS gesteuert Objekt-fit Eigenschaft, die standardmäßig verwendet wird füllen. Es gibt andere Modi, die z. B. statt skalieren, aber seitdem schneiden füllen kommt uns nicht in die Quere (eigentlich kann es nützlich sein), wir lassen es einfach. Wenn Sie planen, Internet Explorer oder Edge zu unterstützen, können Sie ohnehin nichts dagegen tun. Zum Zeitpunkt der Erstellung dieses Artikels werden sie nicht unterstützt Objekt-fit überhaupt.

Beachten Sie jedoch, dass die Frage, wie der Browser den Inhalt skaliert, immer noch umstritten ist. Die CSS-Eigenschaft Bild-Rendering wurde vorgeschlagen, um damit umzugehen, aber es ist immer noch experimentell (wenn überhaupt unterstützt), und es gibt keine bestimmten Skalierungsalgorithmen vor. Nicht nur das, der Browser kann sich dafür entscheiden, es völlig zu vernachlässigen, da dies nur ein Hinweis ist. Dies bedeutet, dass verschiedene Browser vorerst unterschiedliche Skalierungsalgorithmen verwenden, um Ihre Bitmap zu skalieren. Einige von ihnen haben wirklich schreckliche Artefakte, also skalieren Sie nicht zu viel.

Ob wir mit a zeichnen 2d Kontext oder andere Arten von Kontexten (wie webgl), die Leinwand verhält sich fast gleich. Wenn wir möchten, dass unsere kleine Bitmap die gesamte Arbeitsfläche ausfüllt und wir nicht gerne dehnen, sollten wir auf Änderungen der Arbeitsflächengröße achten und die Bitmap-Abmessungen entsprechend anpassen. Lass uns das jetzt machen,

Im Hinblick auf die vorgenommenen Änderungen haben wir dem JavaScript diese beiden Zeilen hinzugefügt:

canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight;

Ja, bei der Verwendung 2d Das Einstellen der internen Bitmap-Bemaßungen auf die Leinwandmaße ist so einfach! Die Leinwand Breite und Höhe überwacht werden und wenn einer von ihnen geschrieben wird (selbst wenn es sich um denselben Wert handelt):

  • Die aktuelle Bitmap wird zerstört.
  • Eine neue mit den neuen Dimensionen wird erstellt.
  • Die neue Bitmap wird mit dem Standardwert (transparentes Schwarz) initialisiert..
  • Jeder zugehörige Kontext wird in seinen ursprünglichen Zustand zurückgesetzt und mit den neu angegebenen Koordinatenraumdimensionen neu initialisiert.

Beachten Sie das, um beide einzustellen Breite und Höhe, Die obigen Schritte werden ausgeführt zweimal! Einmal beim Wechsel Breite und der andere beim Wechsel Höhe. Nein, es gibt keinen anderen Weg, es zu tun, nicht den ich kenne.

Wir haben auch unsere kurze Linie zur neuen Diagonale erweitert,

context.lineTo (canvas.width, canvas.height);

anstatt: 

context.lineTo (30, 30);

Da wir die ursprünglichen 30 * 30-Dimensionen nicht mehr verwenden, werden sie im HTML nicht mehr benötigt:

Wir hätten sie auf sehr kleine Werte (wie 1 * 1) initialisieren lassen können, um den Aufwand für das Erstellen einer Bitmap mit den relativ großen Standarddimensionen (300 * 150) zu sparen, sie zu initialisieren, zu löschen und anschließend eine neue zu erstellen richtige Größe setzen wir in JavaScript.

auf den zweiten Gedanken, lass uns das einfach tun!

Niemand sollte jemals den Unterschied bemerken, aber ich kann die Schuld nicht ertragen!

CSS-Pixel vs. physikalisches Pixel

Ich hätte gerne gesagt, dass es das ist, aber nicht! offsetWidth und offsetHeight werden in CSS-Pixeln angegeben. 

Hier ist der Haken. CSS-Pixel sind nicht physikalische Pixel. Sie sind dichteunabhängige Pixel. Abhängig von der physischen Pixeldichte Ihres Geräts (und Ihrem Browser) kann ein CSS-Pixel einem oder mehreren physischen Pixeln entsprechen.

Wenn Sie ein Full-HD-5-Zoll-Smartphone haben, dann ganz offensichtlich offsetWidth*offsetHeight wäre 640 * 360 statt 1920 * 1080. Sicher, es füllt den Bildschirm aus, aber da die internen Abmessungen auf 640 * 360 eingestellt sind, ist das Ergebnis eine gestreckte Bitmap, die die hohe Auflösung des Geräts nicht voll ausnutzt. Um dies zu beheben, berücksichtigen wir die devicePixelRatio:

var pixelRatio = window.devicePixelRatio? window.devicePixelRatio: 1,0; canvas.width = pixelRatio * canvas.offsetWidth; canvas.height = pixelRatio * canvas.offsetHeight;

devicePixelRatio ist das Verhältnis des CSS-Pixels zum physischen Pixel. Mit anderen Worten, wie viele physische Pixel repräsentiert ein einzelnes CSS-Pixel.

var pixelRatio = window.devicePixelRatio? window.devicePixelRatio: 1,0;

window.devicePixelRatio wird in den meisten modernen Browsern gut unterstützt. Falls dies jedoch undefiniert ist, werden wir auf den Standardwert von zurückgesetzt 1,0.

canvas.width = pixelRatio * canvas.offsetWidth; canvas.height = pixelRatio * canvas.offsetHeight;

Durch das Multiplizieren der CSS-Abmessungen mit dem Pixelverhältnis gelangen wir zurück zu den physikalischen Abmessungen. Jetzt hat unsere interne Bitmap genau die gleiche Größe wie die Leinwand, und es kommt zu keiner Dehnung.

Wenn dein devicePixelRatio ist 1, dann wird es keinen Unterschied geben. Bei jedem anderen Wert ist der Unterschied jedoch signifikant.

Antworten auf Größenänderungen

Das reicht nicht aus, um die Leinwandgröße zu handhaben. Da wir unsere CSS-Abmessungen relativ zur Seitengröße angegeben haben, wirken sich Änderungen in der Seitengröße auf uns aus. Wenn wir einen Desktop-Browser verwenden, kann der Benutzer das Fenster manuell verkleinern. Wenn wir auf einem mobilen Gerät laufen, unterliegen wir Orientierungsänderungen. Nicht zu erwähnen, dass wir in einem laufen können iframe das ändert seine Größe beliebig. Um die Größe der internen Bitmap stets korrekt zu halten, müssen wir auf Änderungen in der Seitengröße (Fenstergröße) achten,

Wir haben unseren Code zur Bitmap-Änderung verschoben:

// Erhalte das Pixelverhältnis des Geräts, var pixelRatio = window.devicePixelRatio? window.devicePixelRatio: 1,0; // Anpassen der Leinwandgröße, canvas.width = pixelRatio * canvas.offsetWidth; canvas.height = pixelRatio * canvas.offsetHeight;

Zu einer separaten Funktion, adjustCanvasBitmapSize:

function adjustCanvasBitmapSize () // Abrufen des Pixelverhältnisses des Geräts, var pixelRatio = window.devicePixelRatio? window.devicePixelRatio: 1,0; if ((canvas.width / pixelRatio)! = canvas.offsetWidth) canvas.width = pixelRatio * canvas.offsetWidth; if ((canvas.height / pixelRatio)! = canvas.offsetHeight) canvas.height = pixelRatio * canvas.offsetHeight; 

mit etwas Modifikation. Da wissen wir, wie teuer es ist, Werte zuzuweisen Breite oder Höhe Es wäre unverantwortlich, dies unnötig zu tun. Jetzt setzen wir nur noch Breite und Höhe wenn sie sich tatsächlich ändern.

Da unsere Funktion auf unsere Leinwand zugreift, erklären wir sie dort, wo sie sie sehen kann. Anfangs wurde es in dieser Zeile erklärt:

var canvas = document.getElementById ("Leinwand");

Dies macht es zu unserem lokalen anonyme Funktion. Wir hätten das einfach entfernen können var Teil und es wäre geworden global (oder genauer gesagt a Eigentum des globales Objekt, die durch zugänglich ist Fenster):

canvas = document.getElementById ("canvas");

Ich rate jedoch dringend davon ab Implizite Erklärung. Wenn Sie immer Ihre Variablen deklarieren, vermeiden Sie viel Verwirrung. Stattdessen werde ich es außerhalb aller Funktionen deklarieren:

var Leinwand; var Kontext;

Dies macht es auch zu einer Eigenschaft des globalen Objekts (mit einem kleinen Unterschied, der uns nicht wirklich stört). Es gibt andere Möglichkeiten, um eine globale Variable in diesem StackOverflow-Thread auschecken zu lassen. 

Oh, und ich habe mich geschlichen Kontext auch da oben! Dies wird sich später als nützlich erweisen.

Lassen Sie uns nun unsere Funktion an das Fenster hängen Größe ändern Veranstaltung:

window.addEventListener ('resize', adjustCanvasBitmapSize);

Von jetzt an, wenn die Fenstergröße geändert wird, adjustCanvasBitmapSize wird genannt. Da das Fenstergrößenereignis jedoch nicht beim ersten Laden ausgelöst wird, ist unsere Bitmap weiterhin 1 * 1. Deshalb müssen wir anrufen adjustCanvasBitmapSize einmal von uns.

adjustCanvasBitmapSize ();

Das kümmert sich so ziemlich darum ... außer, wenn Sie die Größe des Fensters ändern, verschwindet die Linie! Versuchen Sie es in dieser Demo.

Zum Glück ist das zu erwarten. Erinnern Sie sich an die durchgeführten Schritte, wenn die Größe der Bitmap geändert wird? Eine davon war, sie mit transparentem Schwarz zu initialisieren. Das ist hier passiert. Die Bitmap wurde mit transparentem Schwarz überschrieben, und jetzt leuchtet der grüne Hintergrund der Leinwand durch. Dies geschieht, weil wir zu Beginn nur einmal zeichnen. Wenn das Größenänderungsereignis stattfindet, wird der Inhalt gelöscht und nicht neu gezeichnet. Das zu beheben sollte einfach sein. Lassen Sie uns die Linie in eine separate Funktion zeichnen:

function drawScene () // Zeichne unsere Linie, context.beginPath (); context.moveTo (0, 0); context.lineTo (canvas.width, canvas.height); context.stroke (); 

und rufen Sie diese Funktion von innen auf adjustCanvasBitmapSize:

// Alles neu zeichnen, drawScene ();

Auf diese Weise wird unsere Szene jedoch immer wieder neu gezeichnet adjustCanvasBitmapSize heißt, auch wenn keine Dimensionsänderung stattgefunden hat. Um dies zu handhaben, fügen wir eine einfache Überprüfung hinzu:

// Abbruch, wenn sich nichts geändert hat, if (((canvas.width / pixelRatio) == canvas.offsetWidth) && ((canvas.height / pixelRatio) == canvas.offsetHeight)) return; 

Überprüfen Sie das Endergebnis:

Versuchen Sie, die Größe hier zu ändern.

Einschränken der Größenänderungsereignisse

Bis jetzt geht es uns gut! Das Ändern der Größe und das erneute Zeichnen von alles kann jedoch sehr teuer werden, wenn Ihre Leinwand ziemlich groß ist und / oder wenn die Szene kompliziert ist. Darüber hinaus kann die Größenänderung des Fensters mit der Maus die Größenänderung von Ereignissen mit hoher Geschwindigkeit auslösen. Deshalb werden wir es drosseln. Anstatt:

window.addEventListener ('resize', adjustCanvasBitmapSize);

wir werden verwenden:

window.addEventListener ( 'die Größe', function onWindowResize (event) // Warten, bis das Ändern der Größe Ereignisse flood absetzt, wenn (onWindowResize.timeoutId) window.clearTimeout (onWindowResize.timeoutId); onWindowResize.timeoutId = window.setTimeout (adjustCanvasBitmapSize, 600 ;);

Zuerst,

window.addEventListener ('resize', Funktion onWindowResize (event) …);

anstatt direkt anzurufen adjustCanvasBitmapSize wenn der Größe ändern Ereignis wird ausgelöst, wir haben ein verwendet Funktionsausdruck das gewünschte Verhalten definieren. Im Gegensatz zu der Funktion, die wir zuvor für die verwendet haben Belastung Ereignis ist diese Funktion a benannte Funktion. Wenn Sie der Funktion einen Namen geben, können Sie sie leicht aus der Funktion heraus referenzieren.

if (onWindowResize.timeoutId) window.clearTimeout (onWindowResize.timeoutId);

Eigenschaften können wie andere Objekte hinzugefügt werden Funktionsobjekte. Anfänglich, timeoutId ist nicht definiert, Daher wird diese Anweisung nicht ausgeführt. Seien Sie jedoch vorsichtig bei der Verwendung nicht definiert und Null im logische Ausdrücke, weil sie knifflig sein können. Weitere Informationen finden Sie in der ECMAScript-Sprachspezifikation.

Später, timeoutId wird halten timeoutID von einem adjustCanvasBitmapSize Auszeit:

onWindowResize.timeoutId = window.setTimeout (adjustCanvasBitmapSize, 600);

Dies verzögert das Aufrufen adjustCanvasBitmapSize 600 Millisekunden nach dem Auslösen des Ereignisses. Das Ereignis kann jedoch nicht ausgelöst werden. Wenn es innerhalb dieser 600 Millisekunden nicht erneut ausgelöst wird, dann adjustCanvasBitmapSize wird ausgeführt und die Größe der Bitmap wird geändert. Andernfalls, clearTimeout bricht den geplanten ab adjustCanvasBitmapSize und setTimeout plant weitere 600 Millisekunden in der Zukunft. Das Ergebnis ist, solange der Benutzer die Fenstergröße noch ändert, adjustCanvasBitmapSize wird nicht gerufen Wenn der Benutzer eine Weile anhält oder anhält, wird er aufgerufen. Probieren Sie es aus:

Ähm ... ich meine hier.

Warum 600 Millisekunden? Ich denke, es ist nicht zu schnell und nicht zu langsam, aber vor allem funktioniert es gut, wenn Sie Vollbildanimationen ein- / verlassen, was nicht in diesem Tutorial enthalten ist.

Damit ist unser Tutorial für heute abgeschlossen! Wir haben den gesamten Canvas-spezifischen Code behandelt, den wir zum Einrichten unserer Canvas benötigen. Nächstes Mal, wenn Allah es will, werden wir den WebGL-spezifischen Code behandeln und den Shader ausführen. Bis dahin danke fürs Lesen!

Verweise

  • Canvas-Element im W3C-Editorentwurf
  • w3c-Version, in der das Verhalten der Zeichenflächeninitialisierung tatsächlich dokumentiert ist
  • Canvas-Element in der Whatwg-Live-Spezifikation