Web Apps mit Angular und anderen Single Page Application (SPA)-Frameworks sind vielen Entwicklern ein Begriff. Immer mehr klassische Business-Anwendungen werden durch Web-basierte Varianten erweitert oder sogar abgelöst. Dabei wird zumeist festgestellt, dass eine neue Variante der klassischen Lösung in nichts nachsteht – häufig ist es sogar so, dass mehr Nutzer mit ein und derselben Lösung erreicht werden können.
Bisher galt es meist als zu komplex oder nicht möglich, bestehende 3D-, Virtual Reality- oder Augmented Reality-Komponenten einer Anwendung in das Web zu übertragen. Engines wie Babylon.js vereinfachen diesen Prozess erheblich. Heute lassen sich 3D-Inhalte mit bekannten Programmiersprachen, Techniken und Prozessen für das Web verwenden.
In diesem Artikel zeige ich, wie sich Babylon.js und Angular zusammen nutzen lassen, um eine Business-Anwendung mit 3D-Inhalten zu erweitern.
Hinweis: Der gesamte Code kann in diesem GitHub-Repo und auf Stackblitz gefunden werden.
Anpassen des 3D-Inhaltes
Starten wir mit einer einfachen 3D-Szene. Dazu greifen wir auf das Ergebnis aus dem Artikel „Eine Einführung in Grafik-Engines für Webentwickler“ zurück. Dort werden die wichtigsten und notwendigen Eckdaten und Grundlagen erläutert, die wir zum Erstellen einer solchen Szene benötigen.
Der von Babylon.js verwendete Playground hat eine leicht andere Konfiguration als ein übliches Angular-Projekt. Die Anpassung ist uns überlassen, in wenigen Schritten ist die Aufgabe gelöst.
- Exportieren und Herunterladen des Playground-Beispiels
- Auswahl einer Babylon.js-Installation
- Umwandeln von JavaScript zu TypeScript
Der Export könnte nicht leichter fallen. Dazu wählen wir einfach den Download aus und entpacken diesen in ein Wunschverzeichnis. Danach werden ggf. verwendete Assets verschoben und speichern das Script aus der index.html in einer eigenen TypeScript-Datei in einem Angular-Projekt.
Jetzt wird noch Babylon.js selbst im Projekt installiert. Dazu bieten sich am besten die @babylonjs Scoped Packages an. Neben der Modularisierung und Lesbarkeit hat das auch den Vorteil einer gewissen Sicherheit. Nur der passend registrierte Nutzer kann unter dem Scope @babylonjs Pakete veröffentlichen – so ist die Hälfte aller Tippfehler bei einer Installation schon ausgeschlossen, und es können auch andere Pakete installiert werden, ohne noch einmal den Ursprung verifizieren zu müssen.
Sind alle Pakete installiert, machen wir uns an die Typisierung des bestehenden Codes. Am leichtesten fällt das, wenn wir auch direkt Funktionen extrahieren und alles gut in der Szene strukturieren.
Dazu erweitern wir die Standard-Szene von Babylon.js und fügen unseren eigenen Initiierungscode hinzu. Dieser erstellt und platziert alle Grundbestandteile in der Szene – der tatsächliche Definitionsort und auch Zeitpunkt ist stark an den Use-Case gebunden, ggf. muss eine Alternative gewählt werden, die einen anderen Zweck besser erfüllt. In diesem Beispiel wird alles nacheinander initiiert und später vor dem ersten Rendern der Szene ausgeführt.
Ist der Code einmal bereinigt und in Form gegossen, können wir diesen auch direkt in Angular verwenden.
Babylon.js in Angular einbetten
Sollen Angular und Babylon.js zusammen genutzt werden, gibt es ein paar wichtige Punkte zu beachten. Am besten ist es, beide Systeme einzeln zu betrachten und diese so getrennt wie möglich zu halten. Eine kurze Liste der Kernaspekte:
- Trennen Sie Babylon.js von der ngZone und zone.js.
- Beide Frameworks haben ihre eigenen Stärken und Schwächen – so sollten sie auch behandelt werden.
- Versuchen Sie keine Babylon.js-Logik und Probleme in Angular zu lösen oder umgekehrt.
- Eine Grafik-Engine folgt ganz anderen Regeln als ein SPA Framework.
Zurück zum Code: Der Babylon.js-spezifische Code liegt in einer eigenen Klasse vor. Jetzt wird Angular genutzt, um alles anzuzeigen und die Engine an der gewünschten Stelle auszuführen.
Zuerst wird die gewünschte Stelle und Größe via HTML und CSS im DOM definiert. In diesem Fall wird neben der Engine auch ein kleines Feld mit Info angezeigt
Dann greifen wir in Angular auf den Canvas zu und verknüpfen die Engine.
Bevor wir die Engine starten, müssen wir noch sichergehen, dass zone.js nicht requestAnimationFrame-Calls abfängt. Dazu erstellen wir die zone-flags.js-Datei mit den passenden flags disabled und importieren sie in der polyfills.ts vor dem zone.js-Import.
Zu guter Letzt können wir den Render-Loop außerhalb der ngZone starten und sehen das Ergebnis in der Web App.
Natürlich stoppen wir nicht einfach bei der Anzeige diverser 3D-Inhalte in Business-Apps. Der nächste Schritt bildet die Interaktion zwischen Babylon.js und Angular.
Kommunikation zwischen Angular und Babylon.js
Obwohl die Frameworks unterschiedlicher nicht sein könnten, nutzen sie doch die gleichen grundlegenden Techniken – bereitgestellt von JavaScript, TypeScript, sowie dem Browser und dessen APIs. So ist es auch möglich, durch simple Funktionsaufrufe und bekannte Pattern eine lose Kopplung zu erreichen.
Nachdem wir die Engine gestartet und die Szene gerendert haben, können wir auch auf von der Szene bereitgestellte Methoden zurückgreifen.
Sollte einer Szene eine gewünschte Funktionalität fehlen, können wir diese einfach erweitern. So zum Beispiel, um ein vorhandenes Element mittels seiner ID hervorzuheben, oder um eine Liste aller hinzugefügten Mesh bereitzustellen.
Es ist ebenso möglich, Funktionen aufzurufen, um die Szene zu beeinflussen.
Neben einfachen Zugriffen muss auch auf Nutzerinteraktion mit der Szene reagiert werden. Als Beispiel wird uns hier ein Klick auf dargestellte Gegenstände dienen.
User-Interaktion in Babylon.js handhaben
Babylon.js bietet das eigene System der Action Manager, um auf Interaktionen und Events zu reagieren. Dabei können diese spezifisch an einzelne Objekte gebunden oder global abgefangen werden. So kann man Keyboard-Events zur Steuerung oder Shortcuts global handeln und gleichzeitig Klick-Events auf lokale Objekte – ohne, dass sich die verschiedenen Action Manager leicht in die Quere kommen.
Definieren wir zuallererst einen Action Manager auf der Mesh, um per Mesh auf Klick-Events zu reagieren. Dabei ist ein OnPickTrigger mit einem Klick gleichzusetzen.
Die Action-Definierung auf anderen Objekten und der Szene erfolgt analog. Folgend können wir mitteilen, welche weiteren Actions wir handeln möchten und auch direkt wie.
Dabei gibt die Art der Action den Effekt an, und der Trigger ist das, auf was reagiert wird. In diesem Beispiel wird sequenziell per Klick-Event reagiert, um die Position zu ändern.
Babylon.js bietet vorgefertigte Actions für diverse Ereignisse und Zwecke an. Alles andere kann in einer Code-Action gehandhabt werden. Darüber hinaus lassen sich Actions kombinieren und gleichzeitig oder nacheinander abspielen.
Zugleich und zu guter Letzt wird in der ExecuteCodeAction Angular über das ausgewählte Objekt informiert.
In diesem Stackblitz ist das gesamte Ergebnis zu sehen. Eine Babylon.js-Anwendung ist in eine Angular Web App eingebettet. Dazu kommunizieren beide Projektteile über eine minimale API miteinander und könnten leicht wieder getrennt werden.
Angular und Babylon.js – Fazit
Die Integration einer 3D-Anwendung in eine bestehende Angular Web App ist kein Hexenwerk. Wichtig ist es, die Systeme klar zu trennen und so wenig wie nötig zu verbinden. Beachtet man dazu die Eigenarten der verschiedenen Frameworks, steht einer gemeinsamen Verwendung nichts im Wege.
Die wichtigsten Punkte zusammengefasst:
- Die Systeme mit einer klaren API trennen.
- Unbeabsichtigte Beeinflussung verhindern (ngZone, requestAnimationFrame-Calls).
- User-Interaktion mit der Engine direkt in Babylon.js handhaben.
- Game-/3D-Engine spezifische Pattern nutzen.
Ich wünsche Ihnen viel Erfolg mit 3D-Engines in Ihrer Web App!
- Angular und Babylon.js – 3D in jeder Web App - 7. Juli 2021
- Eine Einführung in Grafik-Engines für Webentwickler - 5. Juli 2021