Implement cybernetwork background features: Add routes for serving cybernetwork CSS and JavaScript files, update base.html to include the new CSS and initialization script, and enhance style.css with smooth scrolling and background imports. This update improves the visual experience and functionality of the cybernetwork background in the application.

This commit is contained in:
2025-04-27 15:01:38 +02:00
parent edf3049e42
commit b8ad3aea13
8 changed files with 491 additions and 5 deletions

View File

@@ -0,0 +1,95 @@
/**
* Initialisierungsmodul für den CyberNetwork-Hintergrund
* Importiert und startet die Animation
*/
import CyberNetwork from './cyber-network.js';
// Beim Laden des Dokuments starten
document.addEventListener('DOMContentLoaded', () => {
console.log('CyberNetwork: Initialisierung gestartet');
// Prüfen ob das CSS bereits geladen ist, wenn nicht, dann laden
if (!document.querySelector('link[href*="cybernetwork-bg.css"]')) {
console.log('CyberNetwork: CSS wird geladen');
const cyberNetworkCss = document.createElement('link');
cyberNetworkCss.rel = 'stylesheet';
cyberNetworkCss.href = '/static/css/src/cybernetwork-bg.css';
document.head.appendChild(cyberNetworkCss);
}
// Container-Element für das Netzwerk finden
const container = document.getElementById('cyber-background-container');
if (!container) {
console.error('CyberNetwork: Container #cyber-background-container nicht gefunden!');
return;
}
console.log('CyberNetwork: Container gefunden', container);
// Konfiguration für den Netzwerk-Hintergrund
const networkConfig = {
container: container,
nodeCount: window.innerWidth < 768 ? 15 : 30, // Weniger Nodes auf mobilen Geräten
connectionCount: window.innerWidth < 768 ? 25 : 50,
packetCount: window.innerWidth < 768 ? 8 : 15,
animationSpeed: 1.0
};
// Netzwerk erstellen und initialisieren
const cyberNetwork = new CyberNetwork(networkConfig);
cyberNetwork.init();
console.log('CyberNetwork: Netzwerk initialisiert');
// Globale Referenz für Debug-Zwecke
window.cyberNetwork = cyberNetwork;
});
// Funktion zum manuellen Initialisieren, falls notwendig
export function initCyberNetwork(config = {}) {
console.log('CyberNetwork: Manuelle Initialisierung gestartet');
// CSS laden, falls nicht vorhanden
if (!document.querySelector('link[href*="cybernetwork-bg.css"]')) {
console.log('CyberNetwork: CSS wird geladen (manuell)');
const cyberNetworkCss = document.createElement('link');
cyberNetworkCss.rel = 'stylesheet';
cyberNetworkCss.href = '/static/css/src/cybernetwork-bg.css';
document.head.appendChild(cyberNetworkCss);
}
// Container-Element für das Netzwerk finden
const container = document.getElementById('cyber-background-container');
if (!container) {
console.error('CyberNetwork: Container #cyber-background-container nicht gefunden!');
return null;
}
// Bestehende Instanz zurücksetzen, falls vorhanden
if (window.cyberNetwork) {
console.log('CyberNetwork: Bestehende Instanz wird zurückgesetzt');
window.cyberNetwork.reset();
}
// Netzwerk mit benutzerdefinierten Optionen erstellen
const networkConfig = {
container: container,
nodeCount: window.innerWidth < 768 ? 15 : 30,
connectionCount: window.innerWidth < 768 ? 25 : 50,
packetCount: window.innerWidth < 768 ? 8 : 15,
animationSpeed: 1.0,
...config
};
// Neue Instanz erstellen und initialisieren
const cyberNetwork = new CyberNetwork(networkConfig);
cyberNetwork.init();
console.log('CyberNetwork: Netzwerk manuell initialisiert');
// Globale Referenz aktualisieren
window.cyberNetwork = cyberNetwork;
return cyberNetwork;
}

View File

@@ -0,0 +1,240 @@
/**
* Cyber Network Background Animation
* Generiert dynamisch ein animiertes Netzwerk für den Hintergrund
*/
class CyberNetwork {
constructor(options = {}) {
this.options = {
container: options.container || document.body,
nodeCount: options.nodeCount || 30,
connectionCount: options.connectionCount || 50,
packetCount: options.packetCount || 15,
animationSpeed: options.animationSpeed || 1.0,
...options
};
this.nodes = [];
this.connections = [];
this.packets = [];
this.initialized = false;
this.containerElement = null;
this.networkGridElement = null;
this.glowOverlayElement = null;
}
init() {
if (this.initialized) return;
// Container erstellen
this.containerElement = document.createElement('div');
this.containerElement.className = 'cyber-network-bg';
// Grid erstellen
this.networkGridElement = document.createElement('div');
this.networkGridElement.className = 'network-grid';
this.containerElement.appendChild(this.networkGridElement);
// Glow Overlay erstellen
this.glowOverlayElement = document.createElement('div');
this.glowOverlayElement.className = 'glow-overlay';
this.containerElement.appendChild(this.glowOverlayElement);
// Nodes generieren
this.generateNodes();
// Connections generieren
this.generateConnections();
// Data packets generieren
this.generateDataPackets();
// Container zum DOM hinzufügen
if (typeof this.options.container === 'string') {
const container = document.querySelector(this.options.container);
if (container) {
container.appendChild(this.containerElement);
} else {
document.body.appendChild(this.containerElement);
}
} else {
this.options.container.appendChild(this.containerElement);
}
this.initialized = true;
// Animation starten
window.addEventListener('resize', this.handleResize.bind(this));
this.startAnimationCycle();
}
generateNodes() {
const containerWidth = window.innerWidth;
const containerHeight = window.innerHeight;
for (let i = 0; i < this.options.nodeCount; i++) {
const x = Math.random() * containerWidth;
const y = Math.random() * containerHeight;
const node = document.createElement('div');
node.className = 'node';
node.style.left = `${x}px`;
node.style.top = `${y}px`;
// Größen-Variation für visuelle Tiefe
const size = 2 + Math.random() * 4;
node.style.width = `${size}px`;
node.style.height = `${size}px`;
// Speichern der Position für spätere Referenz
node._data = { x, y, size };
this.containerElement.appendChild(node);
this.nodes.push(node);
}
}
generateConnections() {
for (let i = 0; i < this.options.connectionCount; i++) {
// Zufällige Nodes auswählen
const startNodeIndex = Math.floor(Math.random() * this.nodes.length);
let endNodeIndex;
do {
endNodeIndex = Math.floor(Math.random() * this.nodes.length);
} while (endNodeIndex === startNodeIndex);
const startNode = this.nodes[startNodeIndex];
const endNode = this.nodes[endNodeIndex];
const startData = startNode._data;
const endData = endNode._data;
// Verbindung erstellen
const connection = document.createElement('div');
connection.className = 'connection';
// Position und Rotation berechnen
const dx = endData.x - startData.x;
const dy = endData.y - startData.y;
const length = Math.sqrt(dx * dx + dy * dy);
const angle = Math.atan2(dy, dx) * (180 / Math.PI);
connection.style.width = `${length}px`;
connection.style.left = `${startData.x}px`;
connection.style.top = `${startData.y}px`;
connection.style.transform = `rotate(${angle}deg)`;
// Variation in der Animations-Geschwindigkeit
connection.style.animationDuration = `${3 + Math.random() * 4}s`;
// Speichern der verbundenen Nodes
connection._data = {
startNode: startNodeIndex,
endNode: endNodeIndex,
length
};
this.containerElement.appendChild(connection);
this.connections.push(connection);
}
}
generateDataPackets() {
for (let i = 0; i < this.options.packetCount; i++) {
this.createNewDataPacket();
}
}
createNewDataPacket() {
if (this.connections.length === 0) return;
// Zufällige Verbindung auswählen
const connectionIndex = Math.floor(Math.random() * this.connections.length);
const connection = this.connections[connectionIndex];
const connectionData = connection._data;
const startNode = this.nodes[connectionData.startNode];
const startData = startNode._data;
// Data Packet erstellen
const packet = document.createElement('div');
packet.className = 'data-packet';
// Position auf dem Startknoten
packet.style.left = `${startData.x}px`;
packet.style.top = `${startData.y}px`;
// Zufällige Geschwindigkeit
const travelTime = (4 + Math.random() * 4) / this.options.animationSpeed;
packet.style.setProperty('--travel-time', `${travelTime}s`);
// Ziel-Koordinaten berechnen
const endNode = this.nodes[connectionData.endNode];
const endData = endNode._data;
const travelX = endData.x - startData.x;
const travelY = endData.y - startData.y;
packet.style.setProperty('--travel-x', `${travelX}px`);
packet.style.setProperty('--travel-y', `${travelY}px`);
// Farb-Variation
if (Math.random() > 0.5) {
packet.style.background = 'rgba(76, 223, 255, 0.8)'; // Akzentfarbe
}
this.containerElement.appendChild(packet);
this.packets.push(packet);
// Nach Ende der Animation neues Paket erstellen
setTimeout(() => {
if (this.containerElement.contains(packet)) {
this.containerElement.removeChild(packet);
}
const index = this.packets.indexOf(packet);
if (index > -1) {
this.packets.splice(index, 1);
this.createNewDataPacket();
}
}, travelTime * 1000);
}
handleResize() {
if (!this.initialized) return;
// Bei Größenänderung alles neu generieren
this.reset();
this.init();
}
reset() {
if (!this.initialized) return;
// Alle Elemente entfernen
this.nodes.forEach(node => node.remove());
this.connections.forEach(connection => connection.remove());
this.packets.forEach(packet => packet.remove());
this.nodes = [];
this.connections = [];
this.packets = [];
if (this.containerElement) {
this.containerElement.remove();
}
this.initialized = false;
}
startAnimationCycle() {
// Regelmäßig neue Pakete erstellen für mehr Dynamik
setInterval(() => {
if (this.packets.length < this.options.packetCount * 1.5) {
this.createNewDataPacket();
}
}, 1000 / this.options.animationSpeed);
}
}
// Exportieren als Modul
export default CyberNetwork;