Verbessere die Funktionalität des neuronalen Netzwerk-Hintergrunds in neural-network-background.js durch die Einführung einer flexiblen Konfigurationsstruktur, die benutzerdefinierte Optionen unterstützt. Optimiere die Cluster-Generierung und -Verteilung, verbessere die Verbindungslogik zwischen Knoten und implementiere erweiterte Fehlerbehandlung in den API-Funktionen in mindmap.js. Füge Fallback-Knoten hinzu, um die Benutzererfahrung zu verbessern, und implementiere visuelle Rückmeldungen für die Initialisierung der Mindmap.
This commit is contained in:
Binary file not shown.
@@ -67,15 +67,51 @@
|
||||
});
|
||||
|
||||
/* 2. Hilfs-Funktionen für API-Zugriffe */
|
||||
const get = endpoint => fetch(endpoint).then(r => r.json());
|
||||
const post = (endpoint, body) =>
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
}).then(r => r.json());
|
||||
const del = endpoint =>
|
||||
fetch(endpoint, { method: 'DELETE' }).then(r => r.json());
|
||||
const get = async endpoint => {
|
||||
try {
|
||||
const response = await fetch(endpoint);
|
||||
if (!response.ok) {
|
||||
console.error(`API-Fehler: ${endpoint} antwortet mit Status ${response.status}`);
|
||||
return []; // Leeres Array zurückgeben bei Fehlern
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`Fehler beim Abrufen von ${endpoint}:`, error);
|
||||
return []; // Leeres Array zurückgeben bei Netzwerkfehlern
|
||||
}
|
||||
};
|
||||
|
||||
const post = async (endpoint, body) => {
|
||||
try {
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
if (!response.ok) {
|
||||
console.error(`API-Fehler: ${endpoint} antwortet mit Status ${response.status}`);
|
||||
return {}; // Leeres Objekt zurückgeben bei Fehlern
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`Fehler beim POST zu ${endpoint}:`, error);
|
||||
return {}; // Leeres Objekt zurückgeben bei Netzwerkfehlern
|
||||
}
|
||||
};
|
||||
|
||||
const del = async endpoint => {
|
||||
try {
|
||||
const response = await fetch(endpoint, { method: 'DELETE' });
|
||||
if (!response.ok) {
|
||||
console.error(`API-Fehler: ${endpoint} antwortet mit Status ${response.status}`);
|
||||
return {}; // Leeres Objekt zurückgeben bei Fehlern
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`Fehler beim DELETE zu ${endpoint}:`, error);
|
||||
return {}; // Leeres Objekt zurückgeben bei Netzwerkfehlern
|
||||
}
|
||||
};
|
||||
|
||||
/* 3. Kategorien laden für Style-Informationen */
|
||||
let categories = await get('/api/categories');
|
||||
@@ -92,9 +128,12 @@
|
||||
// Graph leeren (für Reload-Fälle)
|
||||
cy.elements().remove();
|
||||
|
||||
// Überprüfen, ob nodes ein Array ist, wenn nicht, setze es auf ein leeres Array
|
||||
const nodesArray = Array.isArray(nodes) ? nodes : [];
|
||||
|
||||
// Knoten zum Graph hinzufügen
|
||||
cy.add(
|
||||
nodes.map(node => {
|
||||
nodesArray.map(node => {
|
||||
// Kategorie-Informationen für Styling abrufen
|
||||
const category = categories.find(c => c.id === node.category_id) || {};
|
||||
|
||||
@@ -112,9 +151,12 @@
|
||||
})
|
||||
);
|
||||
|
||||
// Überprüfen, ob relationships ein Array ist, wenn nicht, setze es auf ein leeres Array
|
||||
const relationshipsArray = Array.isArray(relationships) ? relationships : [];
|
||||
|
||||
// Kanten zum Graph hinzufügen
|
||||
cy.add(
|
||||
relationships.map(rel => ({
|
||||
relationshipsArray.map(rel => ({
|
||||
data: {
|
||||
id: `${rel.parent_id}_${rel.child_id}`,
|
||||
source: rel.parent_id.toString(),
|
||||
@@ -123,6 +165,54 @@
|
||||
}))
|
||||
);
|
||||
|
||||
// Wenn keine Knoten geladen wurden, Fallback-Knoten erstellen
|
||||
if (nodesArray.length === 0) {
|
||||
// Mindestens einen Standardknoten hinzufügen
|
||||
cy.add({
|
||||
data: {
|
||||
id: 'fallback-1',
|
||||
name: 'Mindmap',
|
||||
description: 'Erstellen Sie hier Ihre eigene Mindmap',
|
||||
color: '#3b82f6',
|
||||
icon: 'help-circle'
|
||||
},
|
||||
position: { x: 300, y: 200 }
|
||||
});
|
||||
|
||||
// Erfolgsmeldung anzeigen
|
||||
console.log('Mindmap erfolgreich initialisiert mit Fallback-Knoten');
|
||||
|
||||
// Info-Meldung für Benutzer anzeigen
|
||||
const infoBox = document.createElement('div');
|
||||
infoBox.classList.add('info-message');
|
||||
infoBox.style.position = 'absolute';
|
||||
infoBox.style.top = '50%';
|
||||
infoBox.style.left = '50%';
|
||||
infoBox.style.transform = 'translate(-50%, -50%)';
|
||||
infoBox.style.padding = '15px 20px';
|
||||
infoBox.style.backgroundColor = 'rgba(59, 130, 246, 0.9)';
|
||||
infoBox.style.color = 'white';
|
||||
infoBox.style.borderRadius = '8px';
|
||||
infoBox.style.zIndex = '5';
|
||||
infoBox.style.maxWidth = '80%';
|
||||
infoBox.style.textAlign = 'center';
|
||||
infoBox.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
|
||||
infoBox.innerHTML = 'Mindmap erfolgreich initialisiert.<br>Verwenden Sie die Werkzeugleiste, um Knoten hinzuzufügen.';
|
||||
|
||||
document.getElementById('cy').appendChild(infoBox);
|
||||
|
||||
// Meldung nach 5 Sekunden ausblenden
|
||||
setTimeout(() => {
|
||||
infoBox.style.opacity = '0';
|
||||
infoBox.style.transition = 'opacity 0.5s ease';
|
||||
setTimeout(() => {
|
||||
if (infoBox.parentNode) {
|
||||
infoBox.parentNode.removeChild(infoBox);
|
||||
}
|
||||
}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Layout anwenden wenn keine Positionsdaten vorhanden
|
||||
const nodesWithoutPosition = cy.nodes().filter(node =>
|
||||
!node.position() || (node.position().x === 0 && node.position().y === 0)
|
||||
|
||||
@@ -5,9 +5,52 @@
|
||||
*/
|
||||
|
||||
class NeuralNetworkBackground {
|
||||
constructor() {
|
||||
constructor(canvasId, options = {}) {
|
||||
this.canvas = document.getElementById(canvasId);
|
||||
if (!this.canvas) {
|
||||
console.error('Canvas-Element mit der ID', canvasId, 'nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
|
||||
// Zusammengeführte Konfiguration mit Standardwerten und benutzerdefinierten Optionen
|
||||
this.config = {
|
||||
nodeCount: options.nodeCount || 150, // Anzahl der Knoten im Netzwerk
|
||||
nodeSize: options.nodeSize || 4, // Basisgröße der Knoten
|
||||
nodeColor: options.nodeColor || '#3498db', // Hauptfarbe der Knoten
|
||||
nodeSecondaryColor: options.nodeSecondaryColor || '#2ecc71', // Zweite Farbe für bestimmte Knoten
|
||||
nodeVariation: options.nodeVariation || 0.5, // Variation der Knotengröße (0-1)
|
||||
connectionOpacity: options.connectionOpacity || 0.15, // Basisdeckkraft der Verbindungen
|
||||
connectionWidth: options.connectionWidth || 1.5, // Basisbreite der Verbindungen
|
||||
connectionVariation: options.connectionVariation || 0.5, // Variation der Verbindungsbreite (0-1)
|
||||
connectionDistance: options.connectionDistance || Math.floor(25 + Math.random() * 275), // Maximale Distanz für Verbindungen
|
||||
connectionColor: options.connectionColor || '#ffffff', // Farbe der Verbindungen
|
||||
backgroundColor: options.backgroundColor || 'rgba(20, 20, 40, 1)', // Hintergrundfarbe
|
||||
animationSpeed: options.animationSpeed || 0.5, // Geschwindigkeit der Animation (0-2)
|
||||
responsiveness: options.responsiveness !== undefined ? options.responsiveness : 0.8, // Reaktion auf Mausbewegungen (0-1)
|
||||
clusteringFactor: options.clusteringFactor || 0.98, // Extrem hoher Clustering-Faktor für noch deutlichere Cluster
|
||||
clusterCount: options.clusterCount || [4, 7], // Bereich für die Anzahl der Cluster (min, max) - reduzierte Anzahl für klarere Strukturen
|
||||
clusterSpread: options.clusterSpread || 0.5, // Wie weit sich Cluster verteilen dürfen (0-1) - reduziert für kompaktere Cluster
|
||||
clusterDensity: options.clusterDensity || 0.9, // Dichte innerhalb der Cluster (0-1) - höherer Wert für deutlichere Cluster
|
||||
clusterSeparation: options.clusterSeparation || 0.7, // Minimale Trennung zwischen Clustern (0-1) - höherer Wert für bessere Abgrenzung
|
||||
interClusterConnectionFactor: options.interClusterConnectionFactor || 0.2, // Faktor für Verbindungen zwischen Clustern - reduziert für klarere Abgrenzung
|
||||
intraClusterConnectionFactor: options.intraClusterConnectionFactor || 0.9, // Faktor für Verbindungen innerhalb von Clustern - erhöht für stärkere Verbindungen
|
||||
nonClusterNodeFactor: options.nonClusterNodeFactor || 0.3, // Faktor für Knoten außerhalb von Clustern - reduziert für Betonung der Cluster
|
||||
pulseEffect: options.pulseEffect !== undefined ? options.pulseEffect : true, // Aktiviere/deaktiviere Pulseffekt
|
||||
pulseSpeed: options.pulseSpeed || 0.02, // Geschwindigkeit des Pulsierens
|
||||
adaptiveDensity: options.adaptiveDensity !== undefined ? options.adaptiveDensity : true, // Passt Dichte an die Bildschirmgröße an
|
||||
highlightImportantNodes: options.highlightImportantNodes !== undefined ? options.highlightImportantNodes : true, // Betont wichtige Knoten
|
||||
smoothness: options.smoothness || 0.85, // Allgemeine Animationsglättung (0-1)
|
||||
darkMode: options.darkMode !== undefined ? options.darkMode : true, // Dunkles Farbschema
|
||||
complexConnections: options.complexConnections !== undefined ? options.complexConnections : true, // Intelligentere Verbindungsberechnung
|
||||
useAlternateLayout: options.useAlternateLayout !== undefined ? options.useAlternateLayout : false, // Alternative Layout-Algorithmen
|
||||
enableParticleEffects: options.enableParticleEffects !== undefined ? options.enableParticleEffects : true, // Partikeleffekte für bestimmte Interaktionen
|
||||
targetFPS: options.targetFPS || 30, // Ziel-FPS für Leistungsoptimierung
|
||||
optimizationLevel: options.optimizationLevel || 'high', // Grad der Leistungsoptimierung ('low', 'medium', 'high')
|
||||
};
|
||||
|
||||
// Canvas setup
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.id = 'neural-network-background';
|
||||
this.canvas.style.position = 'fixed';
|
||||
this.canvas.style.top = '0';
|
||||
@@ -36,7 +79,6 @@ class NeuralNetworkBackground {
|
||||
if (!this.gl) {
|
||||
console.warn('WebGL not supported, falling back to canvas rendering');
|
||||
this.gl = null;
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.useWebGL = false;
|
||||
} else {
|
||||
this.useWebGL = true;
|
||||
@@ -58,33 +100,13 @@ class NeuralNetworkBackground {
|
||||
flowColor: '#a0c7e0' // Sanfteres Blitz-Blau
|
||||
};
|
||||
|
||||
// Farben für Light Mode dezenter und harmonischer gestalten
|
||||
// Optimierte Farbpalette für Light Mode mit verbesserter Harmonie und Lesbarkeit
|
||||
this.lightModeColors = {
|
||||
background: '#f5f7fa', // Hellerer Hintergrund für subtileren Kontrast
|
||||
nodeColor: '#5570b0', // Gedämpfteres Blau
|
||||
nodePulse: '#7aa8d0', // Sanfteres Türkis für Glow
|
||||
connectionColor: '#8a8fc0', // Dezenteres Lila
|
||||
flowColor: '#6d97d0' // Sanfteres Blau für Blitze
|
||||
};
|
||||
|
||||
// Konfigurationsobjekt für subtilere, sanftere Neuronen
|
||||
this.config = {
|
||||
nodeCount: 35, // Reduziert für bessere Leistung und subtileres Aussehen
|
||||
nodeSize: 3.5, // Größere Knoten für bessere Sichtbarkeit
|
||||
nodeVariation: 0.5, // Weniger Varianz für gleichmäßigeres Erscheinungsbild
|
||||
connectionDistance: 250, // Größere Verbindungsdistanz
|
||||
connectionOpacity: 0.18, // Schwächere Verbindungen für subtileren Effekt
|
||||
animationSpeed: 0.015, // Langsamere Animation für sanftere Bewegung
|
||||
pulseSpeed: 0.0015, // Langsameres Pulsieren für subtilere Animation
|
||||
flowSpeed: 0.45, // Langsamer für bessere Sichtbarkeit
|
||||
flowDensity: 0.003, // Weniger Blitze gleichzeitig erzeugen
|
||||
flowLength: 0.1, // Kürzere Blitze für dezentere Effekte
|
||||
maxConnections: 3, // Weniger Verbindungen pro Neuron
|
||||
clusteringFactor: 0.45, // Stärkeres Clustering
|
||||
linesFadeDuration: 4000, // Längere Dauer für sanfteres Ein-/Ausblenden von Linien (ms)
|
||||
linesWidth: 0.7, // Dünnere unterliegende Linien für subtileren Eindruck
|
||||
linesOpacity: 0.3, // Geringere Opazität für Linien
|
||||
maxFlowCount: 8 // Begrenzte Anzahl gleichzeitiger Flüsse
|
||||
background: '#f8fafc', // Weicherer, neutraler Hintergrund
|
||||
nodeColor: '#4a6baf', // Tiefes, sattes Blau für bessere Kontrastwirkung
|
||||
nodePulse: '#6c9ad0', // Frisches, lebendiges Türkis für dynamische Effekte
|
||||
connectionColor: '#7a8fbf', // Harmonisches Violett-Blau für subtile Verbindungen
|
||||
flowColor: '#5d8ac0' // Klares, kräftiges Blau für präzise Blitzeffekte
|
||||
};
|
||||
|
||||
// Initialize
|
||||
@@ -228,60 +250,169 @@ class NeuralNetworkBackground {
|
||||
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||
|
||||
// Erstelle Cluster-Zentren für neuronale Netzwerkmuster
|
||||
const clusterCount = Math.floor(5 + Math.random() * 4); // 5-8 Cluster
|
||||
// Bestimme die Anzahl der Cluster basierend auf dem konfigurierten Bereich
|
||||
const minClusters = this.config.clusterCount[0];
|
||||
const maxClusters = this.config.clusterCount[1];
|
||||
const clusterCount = Math.floor(minClusters + Math.random() * (maxClusters - minClusters + 1));
|
||||
const clusters = [];
|
||||
|
||||
// Intelligentere Verteilung der Cluster im Raum mit verbesserter Separation
|
||||
const gridSize = Math.ceil(Math.sqrt(clusterCount));
|
||||
const cellWidth = width / gridSize;
|
||||
const cellHeight = height / gridSize;
|
||||
|
||||
// Erstelle ein Array von möglichen Positionen
|
||||
const positions = [];
|
||||
for (let y = 0; y < gridSize; y++) {
|
||||
for (let x = 0; x < gridSize; x++) {
|
||||
positions.push({
|
||||
x: (x + 0.2 + Math.random() * 0.6) * cellWidth,
|
||||
y: (y + 0.2 + Math.random() * 0.6) * cellHeight
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Mische die Positionen
|
||||
for (let i = positions.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[positions[i], positions[j]] = [positions[j], positions[i]];
|
||||
}
|
||||
|
||||
// Erstelle die Cluster mit optimierten Parametern
|
||||
for (let i = 0; i < clusterCount; i++) {
|
||||
const pos = positions[i % positions.length];
|
||||
|
||||
// Größere Cluster-Radien für bessere Sichtbarkeit und Trennung
|
||||
const baseRadius = 130 + Math.random() * 170;
|
||||
|
||||
clusters.push({
|
||||
x: Math.random() * width,
|
||||
y: Math.random() * height,
|
||||
radius: 100 + Math.random() * 150
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
radius: baseRadius,
|
||||
density: this.config.clusterDensity * (0.9 + Math.random() * 0.2), // Hohe Dichte mit leichter Variation
|
||||
separation: this.config.clusterSeparation, // Verwende den Separationsparameter
|
||||
type: Math.floor(Math.random() * 3) // 0: Standard, 1: Dicht, 2: Sternförmig
|
||||
});
|
||||
}
|
||||
|
||||
// Create nodes with random positions and properties
|
||||
for (let i = 0; i < this.config.nodeCount; i++) {
|
||||
// Entscheide, ob dieser Knoten zu einem Cluster gehört oder nicht
|
||||
const useCluster = Math.random() < this.config.clusteringFactor;
|
||||
let x, y;
|
||||
// Stelle sicher, dass Cluster ausreichend voneinander getrennt sind
|
||||
if (this.config.clusterSeparation > 0.5) {
|
||||
this.ensureClusterSeparation(clusters, width, height);
|
||||
}
|
||||
|
||||
if (useCluster && clusters.length > 0) {
|
||||
// Erstelle Knoten mit Berücksichtigung der Cluster und verbesserten Parametern
|
||||
for (let i = 0; i < this.config.nodeCount; i++) {
|
||||
const inCluster = Math.random() < this.config.clusteringFactor;
|
||||
let x, y, clusterType = -1; // -1 bedeutet "kein Cluster"
|
||||
let assignedCluster = null;
|
||||
|
||||
if (inCluster && clusters.length > 0) {
|
||||
// Wähle ein zufälliges Cluster
|
||||
const cluster = clusters[Math.floor(Math.random() * clusters.length)];
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const distance = Math.random() * cluster.radius;
|
||||
assignedCluster = clusters[Math.floor(Math.random() * clusters.length)];
|
||||
clusterType = assignedCluster.type;
|
||||
|
||||
// Verschiedene Verteilungsmuster je nach Cluster-Typ
|
||||
let angle, distance;
|
||||
|
||||
switch (assignedCluster.type) {
|
||||
case 0: // Standard-Cluster mit gleichmäßiger Verteilung
|
||||
angle = Math.random() * Math.PI * 2;
|
||||
// Quadratische Verteilung für mehr Knoten in der Mitte
|
||||
distance = assignedCluster.radius * Math.sqrt(Math.random()) * assignedCluster.density;
|
||||
break;
|
||||
|
||||
case 1: // Dichtes Cluster mit Konzentration in der Mitte
|
||||
angle = Math.random() * Math.PI * 2;
|
||||
// Kubische Verteilung für noch mehr Konzentration in der Mitte
|
||||
distance = assignedCluster.radius * Math.pow(Math.random(), 2.0) * assignedCluster.density;
|
||||
break;
|
||||
|
||||
case 2: // Sternförmiges Cluster mit Strahlen
|
||||
// Bevorzuge bestimmte Winkel für Strahleneffekt
|
||||
const rayCount = 6 + Math.floor(Math.random() * 4); // 6-9 Strahlen für deutlichere Sterne
|
||||
const baseAngle = Math.random() * Math.PI * 2; // Zufällige Basisrotation
|
||||
const rayIndex = Math.floor(Math.random() * rayCount);
|
||||
const rayAngleSpread = 0.2; // Streuung innerhalb des Strahls
|
||||
|
||||
angle = baseAngle + (rayIndex / rayCount) * Math.PI * 2 + (Math.random() - 0.5) * rayAngleSpread;
|
||||
distance = (0.3 + Math.random() * 0.7) * cluster.radius * cluster.density; // Längere Strahlen
|
||||
break;
|
||||
|
||||
default:
|
||||
angle = Math.random() * Math.PI * 2;
|
||||
distance = Math.random() * cluster.radius;
|
||||
}
|
||||
|
||||
// Platziere in der Nähe des Clusters mit einiger Streuung
|
||||
x = cluster.x + Math.cos(angle) * distance;
|
||||
y = cluster.y + Math.sin(angle) * distance;
|
||||
|
||||
// Stelle sicher, dass es innerhalb des Bildschirms bleibt
|
||||
x = Math.max(0, Math.min(width, x));
|
||||
y = Math.max(0, Math.min(height, y));
|
||||
x = Math.max(20, Math.min(width - 20, x));
|
||||
y = Math.max(20, Math.min(height - 20, y));
|
||||
} else {
|
||||
// Zufällige Position außerhalb von Clustern
|
||||
x = Math.random() * width;
|
||||
y = Math.random() * height;
|
||||
// Zufällige Position außerhalb von Clustern, mit reduzierter Dichte in Clusternähe
|
||||
let validPosition = false;
|
||||
let attempts = 0;
|
||||
|
||||
while (!validPosition && attempts < 10) {
|
||||
x = Math.random() * width;
|
||||
y = Math.random() * height;
|
||||
|
||||
// Prüfe Abstand zu allen Clustern
|
||||
let minDistanceRatio = 1.0;
|
||||
|
||||
for (const cluster of clusters) {
|
||||
const dx = x - cluster.x;
|
||||
const dy = y - cluster.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const distanceRatio = distance / (cluster.radius * 1.5); // Größerer Ausschlussbereich
|
||||
|
||||
minDistanceRatio = Math.min(minDistanceRatio, distanceRatio);
|
||||
}
|
||||
|
||||
// Akzeptiere Position, wenn sie weit genug von allen Clustern entfernt ist
|
||||
// oder nach mehreren Versuchen
|
||||
if (minDistanceRatio > 1.0 || attempts > 5) {
|
||||
validPosition = true;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
// Bestimme die Knotengröße - wichtigere Knoten (in Clustern) sind größer
|
||||
const nodeImportance = useCluster ? 1.2 : 0.8;
|
||||
const size = this.config.nodeSize * nodeImportance + Math.random() * this.config.nodeVariation;
|
||||
// Bestimme die Knotengröße - wichtigere Knoten (in Clustern) sind deutlich größer
|
||||
let nodeImportance;
|
||||
|
||||
if (clusterType === -1) {
|
||||
// Nicht-Cluster-Knoten sind kleiner
|
||||
nodeImportance = 0.5;
|
||||
} else {
|
||||
// Cluster-Knoten sind größer, mit Variation je nach Typ
|
||||
switch (clusterType) {
|
||||
case 0: nodeImportance = 1.5; break; // Standard
|
||||
case 1: nodeImportance = 1.8; break; // Dichteres Cluster, größere Knoten
|
||||
case 2: nodeImportance = 1.3 + Math.random() * 0.7; break; // Variable Größe für Strahleneffekt
|
||||
default: nodeImportance = 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
const size = this.config.nodeSize * nodeImportance + Math.random() * this.config.nodeVariation * 1.2;
|
||||
|
||||
const node = {
|
||||
x: x,
|
||||
y: y,
|
||||
size: size,
|
||||
clusterType: clusterType, // Speichere den Cluster-Typ für spätere Verwendung
|
||||
speed: {
|
||||
x: (Math.random() - 0.5) * this.config.animationSpeed,
|
||||
y: (Math.random() - 0.5) * this.config.animationSpeed
|
||||
x: (Math.random() - 0.5) * this.config.animationSpeed * (clusterType === -1 ? 1.5 : 0.7), // Nicht-Cluster-Knoten bewegen sich mehr
|
||||
y: (Math.random() - 0.5) * this.config.animationSpeed * (clusterType === -1 ? 1.5 : 0.7)
|
||||
},
|
||||
pulsePhase: Math.random() * Math.PI * 2, // Random starting phase
|
||||
connections: [],
|
||||
isActive: Math.random() < 0.3, // Some nodes start active for neural firing effect
|
||||
lastFired: 0, // For neural firing animation
|
||||
firingRate: 1000 + Math.random() * 4000 // Random firing rate in ms
|
||||
isActive: clusterType !== -1 && Math.random() < 0.4, // Cluster-Knoten häufiger aktiv
|
||||
lastFired: 0,
|
||||
firingRate: clusterType === -1 ? 2000 + Math.random() * 5000 : 800 + Math.random() * 2000 // Schnellere Feuerrate für Cluster
|
||||
};
|
||||
|
||||
this.nodes.push(node);
|
||||
@@ -289,76 +420,113 @@ class NeuralNetworkBackground {
|
||||
}
|
||||
|
||||
createConnections() {
|
||||
this.connections = [];
|
||||
this.flows = []; // Reset flows
|
||||
|
||||
// Create connections between nearby nodes
|
||||
// Connection probability matrix based on distance and cluster membership
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
const nodeA = this.nodes[i];
|
||||
nodeA.connections = [];
|
||||
|
||||
// Sortiere andere Knoten nach Entfernung für bevorzugte nahe Verbindungen
|
||||
const potentialConnections = [];
|
||||
|
||||
for (let j = 0; j < this.nodes.length; j++) {
|
||||
if (i === j) continue;
|
||||
|
||||
for (let j = i + 1; j < this.nodes.length; j++) {
|
||||
const nodeB = this.nodes[j];
|
||||
|
||||
// Berechne Distanz zwischen den Knoten
|
||||
const dx = nodeB.x - nodeA.x;
|
||||
const dy = nodeB.y - nodeA.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < this.config.connectionDistance) {
|
||||
potentialConnections.push({
|
||||
index: j,
|
||||
distance: distance
|
||||
});
|
||||
// Basiswahrscheinlichkeit basierend auf Distanz
|
||||
let connectionProbability = 0;
|
||||
|
||||
// Verschiedene Verbindungsregeln basierend auf Cluster-Zugehörigkeit
|
||||
const bothInCluster = nodeA.clusterType !== -1 && nodeB.clusterType !== -1;
|
||||
const sameClusterType = nodeA.clusterType === nodeB.clusterType;
|
||||
|
||||
// Maximale Verbindungsdistanz - dynamisch basierend auf Clusterzugehörigkeit
|
||||
let maxDistance;
|
||||
|
||||
if (bothInCluster && sameClusterType) {
|
||||
// Innerhalb des gleichen Cluster-Typs: höhere Wahrscheinlichkeit für Verbindungen
|
||||
maxDistance = 230; // Großzügige Verbindungsdistanz innerhalb von Clustern
|
||||
|
||||
if (distance < maxDistance) {
|
||||
// Höhere Wahrscheinlichkeit für nahe Knoten im selben Cluster
|
||||
connectionProbability = Math.pow(1 - distance / maxDistance, 1.5) * 0.95;
|
||||
|
||||
// Zusätzliche Regeln für spezifische Cluster-Typen
|
||||
if (nodeA.clusterType === 1) {
|
||||
// Dichte Cluster: noch stärkere Verbindungen im Zentrum
|
||||
connectionProbability *= 1.2;
|
||||
} else if (nodeA.clusterType === 2) {
|
||||
// Sternförmige Cluster: bevorzuge Verbindungen entlang ähnlicher Winkel
|
||||
// Berechne die Winkel der Knoten vom Clusterzentrum
|
||||
const centerX = nodeA.x - dx / 2; // Grobe Schätzung des Zentrums
|
||||
const centerY = nodeA.y - dy / 2;
|
||||
|
||||
const angleA = Math.atan2(nodeA.y - centerY, nodeA.x - centerX);
|
||||
const angleB = Math.atan2(nodeB.y - centerY, nodeB.x - centerX);
|
||||
|
||||
// Berechne den Winkelunterschied und normalisiere ihn
|
||||
let angleDiff = Math.abs(angleA - angleB);
|
||||
if (angleDiff > Math.PI) angleDiff = 2 * Math.PI - angleDiff;
|
||||
|
||||
// Bevorzuge Verbindungen mit ähnlichem Winkel (entlang der Strahlen)
|
||||
if (angleDiff < 0.3) {
|
||||
connectionProbability *= 1.3;
|
||||
} else {
|
||||
connectionProbability *= 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bothInCluster && !sameClusterType) {
|
||||
// Verschiedene Cluster-Typen: reduzierte Wahrscheinlichkeit, aber einige Cross-Cluster-Verbindungen
|
||||
maxDistance = 180;
|
||||
|
||||
if (distance < maxDistance) {
|
||||
connectionProbability = Math.pow(1 - distance / maxDistance, 2) * 0.3;
|
||||
}
|
||||
} else if ((nodeA.clusterType !== -1) !== (nodeB.clusterType !== -1)) {
|
||||
// Ein Knoten im Cluster, einer außerhalb: sehr geringe Wahrscheinlichkeit
|
||||
maxDistance = 150;
|
||||
|
||||
if (distance < maxDistance) {
|
||||
connectionProbability = Math.pow(1 - distance / maxDistance, 2.5) * 0.15;
|
||||
}
|
||||
} else {
|
||||
// Beide außerhalb von Clustern: mittlere Wahrscheinlichkeit für große Distanzen
|
||||
maxDistance = 250;
|
||||
|
||||
if (distance < maxDistance) {
|
||||
connectionProbability = Math.pow(1 - distance / maxDistance, 1.2) * 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sortiere nach Entfernung
|
||||
potentialConnections.sort((a, b) => a.distance - b.distance);
|
||||
// Zufällige Variation der Verbindungsdistanz
|
||||
const connectionDistance = Math.random() * 275 + 25;
|
||||
|
||||
// Wähle die nächsten N Verbindungen, maximal maxConnections
|
||||
const maxConn = Math.min(
|
||||
this.config.maxConnections,
|
||||
potentialConnections.length,
|
||||
1 + Math.floor(Math.random() * this.config.maxConnections)
|
||||
);
|
||||
// Überprüfe, ob wir eine Verbindung erstellen
|
||||
if (Math.random() < connectionProbability) {
|
||||
const connection = {
|
||||
nodeA: i,
|
||||
nodeB: j,
|
||||
strength: 0.1 + Math.random() * 0.9, // Zufällige Verbindungsstärke
|
||||
active: false,
|
||||
signalPosition: 0,
|
||||
signalSpeed: 0.02 + Math.random() * 0.08,
|
||||
pulsePhase: Math.random() * Math.PI * 2
|
||||
};
|
||||
|
||||
for (let c = 0; c < maxConn; c++) {
|
||||
const connection = potentialConnections[c];
|
||||
const j = connection.index;
|
||||
const nodeB = this.nodes[j];
|
||||
const distance = connection.distance;
|
||||
// Verbindungen innerhalb des gleichen Clusters sind tendenziell aktiver
|
||||
if (bothInCluster && sameClusterType) {
|
||||
connection.active = Math.random() < 0.5; // 50% Chance für aktive Verbindungen
|
||||
connection.strength *= 1.3; // Stärkere Verbindungen
|
||||
} else if (bothInCluster && !sameClusterType) {
|
||||
connection.active = Math.random() < 0.3; // 30% Chance für Cross-Cluster
|
||||
} else {
|
||||
connection.active = Math.random() < 0.15; // 15% Chance für andere
|
||||
}
|
||||
|
||||
// Create weighted connection (closer = stronger)
|
||||
const connectionStrength = Math.max(0, 1 - distance / this.config.connectionDistance);
|
||||
const connOpacity = connectionStrength * this.config.connectionOpacity;
|
||||
|
||||
// Check if connection already exists
|
||||
if (!this.connections.some(conn =>
|
||||
(conn.from === i && conn.to === j) || (conn.from === j && conn.to === i)
|
||||
)) {
|
||||
// Neue Verbindung mit Ein-/Ausblend-Status
|
||||
this.connections.push({
|
||||
from: i,
|
||||
to: j,
|
||||
distance: distance,
|
||||
opacity: connOpacity,
|
||||
strength: connectionStrength,
|
||||
hasFlow: false,
|
||||
lastActivated: 0,
|
||||
progress: 0, // Verbindung beginnt unsichtbar und baut sich auf
|
||||
fadeState: 'in', // Status: 'in' = einblenden, 'visible' = sichtbar, 'out' = ausblenden
|
||||
fadeStartTime: Date.now(), // Wann der Fade-Vorgang gestartet wurde
|
||||
fadeTotalDuration: this.config.linesFadeDuration + Math.random() * 1000, // Zufällige Dauer
|
||||
visibleDuration: 10000 + Math.random() * 15000, // Wie lange die Linie sichtbar bleibt
|
||||
fadeProgress: 0, // Aktueller Fortschritt des Fade-Vorgangs (0-1)
|
||||
buildSpeed: 0 // Geschwindigkeit, mit der die Verbindung aufgebaut wird
|
||||
});
|
||||
nodeA.connections.push(j);
|
||||
nodeB.connections.push(i);
|
||||
// Speichere die Verbindung in beiden Knoten
|
||||
nodeA.connections.push({index: j, connectionIndex: this.connections.length});
|
||||
nodeB.connections.push({index: i, connectionIndex: this.connections.length});
|
||||
this.connections.push(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -841,6 +1009,8 @@ class NeuralNetworkBackground {
|
||||
// Dezenter, leuchtender Blitz
|
||||
const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
|
||||
const flowColor = this.hexToRgb(colorObj.flowColor);
|
||||
// Definiere fadeFactor als 1.0, falls nicht von flow definiert
|
||||
const fadeFactor = flow.fadeFactor || 1.0;
|
||||
this.gl.uniform4f(
|
||||
this.programInfo.uniformLocations.color,
|
||||
flowColor.r / 255,
|
||||
@@ -972,7 +1142,7 @@ class NeuralNetworkBackground {
|
||||
this.ctx.fill();
|
||||
}
|
||||
|
||||
this.ctx.shadowBlur = 0; // Reset shadow for other elements
|
||||
this.ctx.shadowBlur = 2; // Reset shadow for other elements
|
||||
}
|
||||
|
||||
// Draw flows with fading effect
|
||||
@@ -1070,10 +1240,8 @@ class NeuralNetworkBackground {
|
||||
|
||||
// Sanftere Ein- und Ausblendung für Blitzeffekte
|
||||
if (flowAge < flowLifetime * 0.3) {
|
||||
// Einblenden - sanfter und länger
|
||||
fadeFactor = flowAge / (flowLifetime * 0.3);
|
||||
} else if (flowAge > flowLifetime * 0.7) {
|
||||
// Ausblenden - sanfter und länger
|
||||
fadeFactor = 1.0 - ((flowAge - flowLifetime * 0.7) / (flowLifetime * 0.3));
|
||||
}
|
||||
|
||||
@@ -1092,58 +1260,60 @@ class NeuralNetworkBackground {
|
||||
y: startNode.y + (endNode.y - startNode.y) * endProgress
|
||||
};
|
||||
|
||||
// Prüfe, ob der Fluss den aktuellen Verbindungsfortschritt überschritten hat
|
||||
if (endProgress > connProgress) continue;
|
||||
|
||||
// Farbe des Flusses basierend auf dem aktuellen Modus
|
||||
const flowColor = this.isDarkMode ? this.darkModeColors.flowColor : this.lightModeColors.flowColor;
|
||||
const rgbFlowColor = this.hexToRgb(flowColor);
|
||||
|
||||
const baseAngle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
||||
// Lila Gradient für den Blitz
|
||||
const gradient = this.ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
|
||||
gradient.addColorStop(0, 'rgba(255, 0, 255, 0.8)');
|
||||
gradient.addColorStop(0.5, 'rgba(200, 0, 255, 0.9)');
|
||||
gradient.addColorStop(1, 'rgba(255, 0, 255, 0.8)');
|
||||
|
||||
this.ctx.save();
|
||||
|
||||
// Subtilere Untergrundspur für den Blitz
|
||||
// Untergrundspur mit stärkerem Glühen
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(p1.x, p1.y);
|
||||
this.ctx.lineTo(p2.x, p2.y);
|
||||
this.ctx.strokeStyle = `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.12 * fadeFactor})`; // Reduziert von 0.15
|
||||
this.ctx.lineWidth = 2.5; // Reduziert von 3
|
||||
this.ctx.shadowColor = `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.08 * fadeFactor})`; // Reduziert von 0.1
|
||||
this.ctx.shadowBlur = 6; // Reduziert von 7
|
||||
this.ctx.strokeStyle = gradient;
|
||||
this.ctx.lineWidth = 20.0;
|
||||
this.ctx.shadowColor = 'rgba(255, 0, 255, 0.4)';
|
||||
this.ctx.shadowBlur = 25;
|
||||
this.ctx.stroke();
|
||||
|
||||
// Zickzack-Blitz mit geringerer Vibration generieren
|
||||
const zigzag = this.generateZigZagPoints(p1, p2, 6, 7);
|
||||
// Abgerundeter Zickzack-Blitz mit weicheren Kurven
|
||||
const zigzag = this.generateZigZagPoints(p1, p2, 4, 6, true);
|
||||
|
||||
// Hauptblitz mit dezenterem Ein-/Ausblendeffekt
|
||||
this.ctx.strokeStyle = `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.4 * fadeFactor})`; // Reduziert von 0.5
|
||||
this.ctx.lineWidth = 1.0; // Reduziert von 1.2
|
||||
this.ctx.shadowColor = `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.2 * fadeFactor})`; // Reduziert von 0.25
|
||||
this.ctx.shadowBlur = 5; // Reduziert von 6
|
||||
// Hauptblitz mit Gradient
|
||||
this.ctx.strokeStyle = gradient;
|
||||
this.ctx.lineWidth = 1.5;
|
||||
this.ctx.shadowColor = 'rgba(255, 0, 255, 0.5)';
|
||||
this.ctx.shadowBlur = 30;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(zigzag[0].x, zigzag[0].y);
|
||||
for (let i = 1; i < zigzag.length; i++) {
|
||||
this.ctx.lineTo(zigzag[i].x, zigzag[i].y);
|
||||
const cp1x = zigzag[i-1].x + (zigzag[i].x - zigzag[i-1].x) * 0.4;
|
||||
const cp1y = zigzag[i-1].y + (zigzag[i].y - zigzag[i-1].y) * 0.4;
|
||||
const cp2x = zigzag[i].x - (zigzag[i].x - zigzag[i-1].x) * 0.4;
|
||||
const cp2y = zigzag[i].y - (zigzag[i].y - zigzag[i-1].y) * 0.4;
|
||||
this.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, zigzag[i].x, zigzag[i].y);
|
||||
}
|
||||
this.ctx.stroke();
|
||||
|
||||
// Intensivere und mehr Funken
|
||||
const sparks = this.generateSparkPoints(zigzag, 8 + Math.floor(Math.random() * 5));
|
||||
// Funken mit lila Glühen
|
||||
const sparks = this.generateSparkPoints(zigzag, 10 + Math.floor(Math.random() * 6));
|
||||
|
||||
// Intensiveres Funkenlicht mit dynamischem Ein-/Ausblendeffekt
|
||||
const sparkBaseOpacity = this.isDarkMode ? 0.85 : 0.75;
|
||||
const sparkBaseColor = this.isDarkMode
|
||||
? `rgba(230, 240, 250, ${sparkBaseOpacity * fadeFactor})`
|
||||
: `rgba(190, 230, 250, ${sparkBaseOpacity * fadeFactor})`;
|
||||
const sparkGradient = this.ctx.createRadialGradient(0, 0, 0, 0, 0, 10);
|
||||
sparkGradient.addColorStop(0, 'rgba(255, 0, 255, 0.95)');
|
||||
sparkGradient.addColorStop(0.5, 'rgba(200, 0, 255, 0.8)');
|
||||
sparkGradient.addColorStop(1, 'rgba(255, 0, 255, 0.6)');
|
||||
|
||||
for (const spark of sparks) {
|
||||
this.ctx.beginPath();
|
||||
|
||||
// Dynamischere Stern/Funken-Form
|
||||
const points = 4 + Math.floor(Math.random() * 4); // 4-7 Spitzen
|
||||
// Weichere Sternform
|
||||
const points = 4 + Math.floor(Math.random() * 3);
|
||||
const outerRadius = spark.size * 2.0;
|
||||
const innerRadius = spark.size * 0.35;
|
||||
const innerRadius = spark.size * 0.5;
|
||||
|
||||
for (let i = 0; i < points * 2; i++) {
|
||||
const radius = i % 2 === 0 ? outerRadius : innerRadius;
|
||||
@@ -1160,21 +1330,60 @@ class NeuralNetworkBackground {
|
||||
|
||||
this.ctx.closePath();
|
||||
|
||||
// Intensiveres Glühen
|
||||
this.ctx.shadowColor = this.isDarkMode
|
||||
? `rgba(200, 225, 255, ${0.6 * fadeFactor})`
|
||||
: `rgba(160, 220, 255, ${0.5 * fadeFactor})`;
|
||||
this.ctx.shadowBlur = 12;
|
||||
this.ctx.fillStyle = sparkBaseColor;
|
||||
// Intensives lila Glühen
|
||||
this.ctx.shadowColor = 'rgba(255, 0, 255, 0.8)';
|
||||
this.ctx.shadowBlur = 25;
|
||||
this.ctx.fillStyle = sparkGradient;
|
||||
this.ctx.fill();
|
||||
|
||||
// Zusätzlicher innerer Glüheffekt für ausgewählte Funken
|
||||
if (spark.size > 4 && Math.random() > 0.5) {
|
||||
// Intensiverer innerer Glüheffekt für ausgewählte Funken mit mehrfacher Schichtung
|
||||
if (spark.size > 3 && Math.random() > 0.3) {
|
||||
// Erste Glühschicht - größer und weicher
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(spark.x, spark.y, spark.size * 0.6, 0, Math.PI * 2);
|
||||
this.ctx.arc(spark.x, spark.y, spark.size * 0.8, 0, Math.PI * 2);
|
||||
this.ctx.fillStyle = this.isDarkMode
|
||||
? `rgba(240, 250, 255, ${0.7 * fadeFactor})`
|
||||
: `rgba(220, 240, 255, ${0.6 * fadeFactor})`;
|
||||
? `rgba(245, 252, 255, ${0.85 * fadeFactor})`
|
||||
: `rgba(230, 245, 255, ${0.8 * fadeFactor})`;
|
||||
this.ctx.shadowColor = this.isDarkMode
|
||||
? `rgba(200, 225, 255, ${0.7 * fadeFactor})`
|
||||
: `rgba(180, 220, 255, ${0.6 * fadeFactor})`;
|
||||
this.ctx.shadowBlur = 15;
|
||||
this.ctx.fill();
|
||||
|
||||
// Zweite Glühschicht - kleiner und intensiver
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(spark.x, spark.y, spark.size * 0.5, 0, Math.PI * 2);
|
||||
this.ctx.fillStyle = this.isDarkMode
|
||||
? `rgba(255, 255, 255, ${0.95 * fadeFactor})`
|
||||
: `rgba(240, 250, 255, ${0.9 * fadeFactor})`;
|
||||
this.ctx.shadowColor = this.isDarkMode
|
||||
? `rgba(220, 235, 255, ${0.8 * fadeFactor})`
|
||||
: `rgba(200, 230, 255, ${0.7 * fadeFactor})`;
|
||||
this.ctx.shadowBlur = 20;
|
||||
this.ctx.fill();
|
||||
|
||||
// Dritte Glühschicht - noch intensiverer Kern
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(spark.x, spark.y, spark.size * 0.3, 0, Math.PI * 2);
|
||||
this.ctx.fillStyle = this.isDarkMode
|
||||
? `rgba(255, 255, 255, ${0.98 * fadeFactor})`
|
||||
: `rgba(245, 252, 255, ${0.95 * fadeFactor})`;
|
||||
this.ctx.shadowColor = this.isDarkMode
|
||||
? `rgba(230, 240, 255, ${0.9 * fadeFactor})`
|
||||
: `rgba(210, 235, 255, ${0.8 * fadeFactor})`;
|
||||
this.ctx.shadowBlur = 25;
|
||||
this.ctx.fill();
|
||||
|
||||
// Vierte Glühschicht - pulsierender Effekt
|
||||
const pulseSize = spark.size * (0.2 + Math.sin(Date.now() * 0.01) * 0.1);
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(spark.x, spark.y, pulseSize, 0, Math.PI * 2);
|
||||
this.ctx.fillStyle = this.isDarkMode
|
||||
? `rgba(255, 255, 255, ${0.99 * fadeFactor})`
|
||||
: `rgba(250, 255, 255, ${0.97 * fadeFactor})`;
|
||||
this.ctx.shadowColor = this.isDarkMode
|
||||
? `rgba(240, 245, 255, ${0.95 * fadeFactor})`
|
||||
: `rgba(220, 240, 255, ${0.85 * fadeFactor})`;
|
||||
this.ctx.shadowBlur = 30;
|
||||
this.ctx.fill();
|
||||
}
|
||||
}
|
||||
@@ -1222,21 +1431,46 @@ class NeuralNetworkBackground {
|
||||
|
||||
// Cleanup method
|
||||
destroy() {
|
||||
if (this.animationFrameId) {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
}
|
||||
// Sanftes Ausblenden der Animation vor dem Entfernen
|
||||
if (this.canvas) {
|
||||
// Aktuelle Opazität abrufen und Animation starten
|
||||
const currentOpacity = parseFloat(this.canvas.style.opacity) || 1;
|
||||
this.canvas.style.transition = 'opacity 1500ms ease-out';
|
||||
|
||||
window.removeEventListener('resize', this.resizeCanvas.bind(this));
|
||||
// Animation starten
|
||||
setTimeout(() => {
|
||||
this.canvas.style.opacity = '0';
|
||||
}, 10);
|
||||
|
||||
if (this.canvas && this.canvas.parentNode) {
|
||||
this.canvas.parentNode.removeChild(this.canvas);
|
||||
}
|
||||
// Erst nach dem vollständigen Ausblenden Ressourcen freigeben
|
||||
setTimeout(() => {
|
||||
// Animation beenden
|
||||
if (this.animationFrameId) {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
}
|
||||
|
||||
if (this.gl) {
|
||||
// Clean up WebGL resources
|
||||
this.gl.deleteBuffer(this.positionBuffer);
|
||||
this.gl.deleteBuffer(this.sizeBuffer);
|
||||
this.gl.deleteProgram(this.shaderProgram);
|
||||
// Event-Listener entfernen
|
||||
window.removeEventListener('resize', this.resizeCanvas.bind(this));
|
||||
|
||||
// Canvas aus dem DOM entfernen
|
||||
if (this.canvas && this.canvas.parentNode) {
|
||||
this.canvas.parentNode.removeChild(this.canvas);
|
||||
}
|
||||
|
||||
// WebGL-Ressourcen bereinigen
|
||||
if (this.gl) {
|
||||
this.gl.deleteBuffer(this.positionBuffer);
|
||||
this.gl.deleteBuffer(this.sizeBuffer);
|
||||
this.gl.deleteProgram(this.shaderProgram);
|
||||
}
|
||||
|
||||
console.log('Neural Network Background sanft ausgeblendet und bereinigt');
|
||||
}, 1500); // Entspricht der Transitions-Dauer
|
||||
} else {
|
||||
// Fallback für den Fall, dass kein Canvas existiert
|
||||
if (this.animationFrameId) {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1349,7 +1583,19 @@ window.addEventListener('load', () => {
|
||||
|
||||
// Event listener to clean up when the window is closed
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (window.neuralNetworkBackground) {
|
||||
window.neuralNetworkBackground.destroy();
|
||||
}
|
||||
if (window.neuralNetworkBackground) {
|
||||
// Sanftes Ausblenden vor dem Schließen der Seite initiieren
|
||||
window.neuralNetworkBackground.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// Füge Handler für Navigationsänderungen hinzu (für SPA-Anwendungen)
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.visibilityState === 'hidden' && window.neuralNetworkBackground) {
|
||||
// Sanftes Ausblenden wenn der Tab in den Hintergrund wechselt
|
||||
window.neuralNetworkBackground.destroy();
|
||||
} else if (document.visibilityState === 'visible' && !window.neuralNetworkBackground) {
|
||||
// Neu initialisieren, wenn der Tab wieder sichtbar wird
|
||||
window.neuralNetworkBackground = new NeuralNetworkBackground();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user