Kategorien
Angular three.js WebGL

Angular three.js Demo

Mit den folgenden Schritten kann man eine webgl three.js Komponenete in Angular erstellen:

  1. Installiere Three.js als Abhängigkeit:
npm install three

2. Installiere die Three.js-Typdefinitionen, um die Typüberprüfung und Code-Intelligenz in deinem Angular-Projekt zu verbessern. Führe den folgenden Befehl aus:

npm install @types/three --save-dev

3. Erstelle einer Angular Komponente

import {Component, OnInit} from '@angular/core';
import * as THREE from 'three';
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {TextGeometry} from "three/examples/jsm/geometries/TextGeometry";
import {FontLoader} from "three/examples/jsm/loaders/FontLoader";
import {Mesh, MeshBasicMaterial, Scene, SphereGeometry, WebGLRenderer} from "three";

@Component({
  selector: 'app-mindmap',
  template: `
    <div id="mindmap" #mindmap></div>
  `,
  styles: [
    '#mindmap{  width: 1200px;height: 400px;display: block;}'
  ]
})
export class MindmapComponent implements OnInit {

  nodeA!: Mesh<SphereGeometry, MeshBasicMaterial>;
  nodeB!: Mesh<SphereGeometry, MeshBasicMaterial>;
  nodeC!: Mesh<SphereGeometry, MeshBasicMaterial>;

  @ViewChild('mindmap', { static: true }) mindmap?: ElementRef;

  private font: any;

  width = 1200;
  height = 400;

  private renderer!: WebGLRenderer;
  private scene!: Scene;

  ngOnInit(): void {

    // Laden der Schriftart
    var fontLoader = new FontLoader();
    fontLoader.load('assets/font/helvetiker_regular.typeface.json', (font: any) => {
      this.font = font;
      this.drawMindmap();
    });
  }

  private drawMindmap()
  {

// Initialisieren der Szene, Kamera und Renderer
    this.initialize();

    this.createNodes();

    // Erstellen der Textgeometrie mit der geladenen Schriftart
    this.createText();

    this.addControls();


    this.createConnections();
  }

  private initialize() {
    this.scene = new THREE.Scene();

    this.renderer = new THREE.WebGLRenderer({antialias: true});
    this.renderer.setSize(this.width, this.height);
    this.renderer.setClearColor(0xffffff); // Hintergrundfarbe auf Weiß setzen

  this.mindmap?.nativeElement.appendChild(this.renderer.domElement);

    const axesHelper = new THREE.AxesHelper( 1 );
    this.scene.add( axesHelper );
  }

  private createNodes() {
// Erstellen der Knoten-Geometrie und Materialien
    var geometry = new THREE.SphereGeometry(0.05, 32, 32); // Größe der Kugeln auf 10% reduzieren
    var material = new THREE.MeshBasicMaterial({color: 0x0000ff}); // Dunkelblau für das Material

// Erstellen der Knoten A
    var nodeA = new THREE.Mesh(geometry, material);
    this.scene.add(nodeA);
    nodeA.position.set(-1, 0, 0);

    this.nodeA = nodeA;

// Erstellen der Knoten B
    var nodeB = new THREE.Mesh(geometry, material);
    this.scene.add(nodeB);
    nodeB.position.set(0, 1, 0); // Knoten B nach unten verschieben

    this.nodeB = nodeB;

// Erstellen der Knoten C
    var nodeC = new THREE.Mesh(geometry, material);
    this.scene.add(nodeC);
    nodeC.position.set(1, 0, 0);

    this.nodeC= nodeC;
  }

  private addControls() {
    /**
     *     fov: Das Sichtfeld (Field of View) in Grad. Es definiert den vertikalen Erfassungswinkel der Kamera. Ein größerer Wert führt zu einem breiteren Sichtfeld und umgekehrt.
     *     aspect: Das Seitenverhältnis (Aspect Ratio) der Kamera. Es wird üblicherweise als Breite durch Höhe definiert. In diesem Fall wird this.width / this.height verwendet, wobei this.width und this.height die Breite und Höhe des Anzeigebereichs (Canvas oder Fenster) sind. Das Seitenverhältnis beeinflusst das Verhältnis der horizontalen zur vertikalen Sicht und wird verwendet, um die Szene korrekt zu skalieren.
     *     near: Der nahe Clip-Abstand. Objekte, die näher an der Kamera liegen als dieser Wert, werden abgeschnitten und nicht gerendert.
     *     far: Der ferne Clip-Abstand. Objekte, die weiter entfernt von der Kamera liegen als dieser Wert, werden ebenfalls abgeschnitten.
     */
    var camera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000);

    // Berechnen der Bounding Box der Knoten
    const boundingBox = new THREE.Box3().setFromObject(this.nodeA);
    boundingBox.expandByObject(this.nodeB);
    boundingBox.expandByObject(this.nodeC);
    const center = new THREE.Vector3();
    const size = new THREE.Vector3();
    boundingBox.getCenter(center);
    boundingBox.getSize(size);

// Berechnen des Kameraabstands basierend auf der Größe der Bounding Box
    const maxDimension = Math.max(size.x, size.y, size.z);
    const cameraDistance = maxDimension ; // Faktor 2 für etwas zusätzlichen Abstand
    camera.position.z = cameraDistance;

// Setzen der Kamera-Position auf den Schwerpunkt der Bounding Box
    camera.lookAt(center);

// Anpassen des Kamera-Zooms
    const cameraZoom = 1.5; // Passen Sie den Zoomwert an Ihre Anforderungen an
    camera.zoom = cameraZoom;
    camera.updateProjectionMatrix();

      // Maussteuerung für das Drehen der Szene hinzufügen
    var controls = new OrbitControls(camera, this.renderer.domElement);
    controls.target.set(0, 0, 0); // Drehachse in der Mitte des Bildes

    // Animations-Schleife
    const animate = () => {
      requestAnimationFrame(animate);

      // Animationen oder Interaktionen hier einfügen

      this.renderer.render(this.scene, camera);
    }
    animate();
  }

  private createText() {
    var textMaterial = new THREE.MeshBasicMaterial({color: 0x000000}); // Schriftfarbe auf Schwarz setzen

    var textGeometryA = new TextGeometry("A", {
      font: this.font,
      size: 0.05, // Größe der Schrift auf 5% reduzieren
      height: 0.005, // Höhe der Schrift auf 0.5% reduzieren
    });
    var textA = new THREE.Mesh(textGeometryA, textMaterial);
    textA.position.copy(this.nodeA.position);
    textA.position.y -= 0.2; // Knoten A TextNode nach unten verschieben
    this.scene.add(textA);

    var textGeometryB = new TextGeometry("B", {
      font: this.font,
      size: 0.05,
      height: 0.005,
    });
    var textB = new THREE.Mesh(textGeometryB, textMaterial);
    textB.position.copy(this.nodeB.position);
    textB.position.y -= 0.2; // Knoten B TextNode nach unten verschieben
    this.scene.add(textB);

    var textGeometryC = new TextGeometry("C", {
      font: this.font,
      size: 0.05,
      height: 0.005,
    });
    var textC = new THREE.Mesh(textGeometryC, textMaterial);
    textC.position.copy(this.nodeC.position);
    textC.position.y -= 0.2; // Knoten C TextNode nach unten verschieben
    this.scene.add(textC);
  }

  private createConnections() {
    // Erstellen der Beziehungslinien
    var lineMaterial = new THREE.LineBasicMaterial({color: 0x000000, linewidth: 4}); // Linienfarbe auf Schwarz und Linienbreite auf 4 setzen

    var lineABGeometry = new THREE.BufferGeometry().setFromPoints([this.nodeA.position, this.nodeB.position]);
    var lineAB = new THREE.Line(lineABGeometry, lineMaterial);
    this.scene.add(lineAB);

    var lineBCGeometry = new THREE.BufferGeometry().setFromPoints([this.nodeB.position, this.nodeC.position]);
    var lineBC = new THREE.Line(lineBCGeometry, lineMaterial);
    this.scene.add(lineBC);

    var lineCAGeometry = new THREE.BufferGeometry().setFromPoints([this.nodeC.position, this.nodeA.position]);
    var lineCA = new THREE.Line(lineCAGeometry, lineMaterial);
    this.scene.add(lineCA);
  }
}

Ergebnis: