232 lines
7.2 KiB
JavaScript
232 lines
7.2 KiB
JavaScript
// Animated Network Background
|
|
let canvas, ctx, networkImage;
|
|
let isImageLoaded = false;
|
|
let animationSpeed = 0.0003; // Reduzierte Geschwindigkeit für sanftere Rotation
|
|
let scaleSpeed = 0.0001; // Reduzierte Geschwindigkeit für sanftere Skalierung
|
|
let opacitySpeed = 0.0002; // Reduzierte Geschwindigkeit für sanftere Opazitätsänderung
|
|
let rotation = 0;
|
|
let scale = 1;
|
|
let opacity = 0.7; // Höhere Basisopazität für bessere Sichtbarkeit
|
|
let scaleDirection = 1;
|
|
let opacityDirection = 1;
|
|
let animationFrameId = null;
|
|
let isDarkMode = document.documentElement.classList.contains('dark');
|
|
let loadAttempts = 0;
|
|
const MAX_LOAD_ATTEMPTS = 2;
|
|
|
|
// Initialize the canvas and load the image
|
|
function initNetworkBackground() {
|
|
// Create canvas element if it doesn't exist
|
|
if (!document.getElementById('network-background')) {
|
|
canvas = document.createElement('canvas');
|
|
canvas.id = 'network-background';
|
|
canvas.style.position = 'fixed';
|
|
canvas.style.top = '0';
|
|
canvas.style.left = '0';
|
|
canvas.style.width = '100%';
|
|
canvas.style.height = '100%';
|
|
canvas.style.zIndex = '-5'; // Höher als -10 für den full-page-bg
|
|
canvas.style.pointerEvents = 'none'; // Stellt sicher, dass der Canvas keine Mausinteraktionen blockiert
|
|
document.body.appendChild(canvas);
|
|
} else {
|
|
canvas = document.getElementById('network-background');
|
|
}
|
|
|
|
// Set canvas size to window size with pixel ratio consideration
|
|
resizeCanvas();
|
|
|
|
// Get context with alpha enabled
|
|
ctx = canvas.getContext('2d', { alpha: true });
|
|
|
|
// Load the network image - versuche zuerst die SVG-Version
|
|
networkImage = new Image();
|
|
networkImage.crossOrigin = "anonymous"; // Vermeidet CORS-Probleme
|
|
|
|
// Keine Bilder laden, direkt Fallback-Hintergrund verwenden
|
|
console.log("Verwende einfachen Hintergrund ohne Bilddateien");
|
|
isImageLoaded = true; // Animation ohne Hintergrundbild starten
|
|
startAnimation();
|
|
|
|
// Handle window resize
|
|
window.addEventListener('resize', debounce(resizeCanvas, 250));
|
|
|
|
// Überwache Dark Mode-Änderungen
|
|
document.addEventListener('darkModeToggled', function(event) {
|
|
isDarkMode = event.detail.isDark;
|
|
});
|
|
}
|
|
|
|
// Hilfsfunktion zur Reduzierung der Resize-Event-Aufrufe
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function() {
|
|
const context = this;
|
|
const args = arguments;
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(function() {
|
|
func.apply(context, args);
|
|
}, wait);
|
|
};
|
|
}
|
|
|
|
// Resize canvas to match window size with proper pixel ratio
|
|
function resizeCanvas() {
|
|
if (!canvas) return;
|
|
|
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
const width = window.innerWidth;
|
|
const height = window.innerHeight;
|
|
|
|
// Set display size (css pixels)
|
|
canvas.style.width = width + 'px';
|
|
canvas.style.height = height + 'px';
|
|
|
|
// Set actual size in memory (scaled for pixel ratio)
|
|
canvas.width = width * pixelRatio;
|
|
canvas.height = height * pixelRatio;
|
|
|
|
// Scale context to match pixel ratio
|
|
if (ctx) {
|
|
ctx.scale(pixelRatio, pixelRatio);
|
|
}
|
|
|
|
// Wenn Animation läuft und Bild geladen, zeichne erneut
|
|
if (isImageLoaded && animationFrameId) {
|
|
drawNetworkImage();
|
|
}
|
|
}
|
|
|
|
// Start animation
|
|
function startAnimation() {
|
|
if (animationFrameId) {
|
|
cancelAnimationFrame(animationFrameId);
|
|
}
|
|
|
|
// Start animation loop
|
|
animate();
|
|
}
|
|
|
|
// Draw network image
|
|
function drawNetworkImage() {
|
|
if (!ctx) return;
|
|
|
|
// Clear canvas with proper clear method
|
|
ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1));
|
|
|
|
// Save context state
|
|
ctx.save();
|
|
|
|
// Move to center of canvas
|
|
ctx.translate(canvas.width / (2 * (window.devicePixelRatio || 1)), canvas.height / (2 * (window.devicePixelRatio || 1)));
|
|
|
|
// Rotate
|
|
ctx.rotate(rotation);
|
|
|
|
// Scale
|
|
ctx.scale(scale, scale);
|
|
|
|
// Set global opacity, angepasst für Dark Mode
|
|
ctx.globalAlpha = isDarkMode ? opacity : opacity * 0.8;
|
|
|
|
if (isImageLoaded && networkImage.complete) {
|
|
// Bildgröße berechnen, um den Bildschirm abzudecken
|
|
const imgAspect = networkImage.width / networkImage.height;
|
|
const canvasAspect = canvas.width / canvas.height;
|
|
|
|
let drawWidth, drawHeight;
|
|
|
|
if (canvasAspect > imgAspect) {
|
|
drawWidth = canvas.width / (window.devicePixelRatio || 1);
|
|
drawHeight = drawWidth / imgAspect;
|
|
} else {
|
|
drawHeight = canvas.height / (window.devicePixelRatio || 1);
|
|
drawWidth = drawHeight * imgAspect;
|
|
}
|
|
|
|
// Draw image centered
|
|
ctx.drawImage(
|
|
networkImage,
|
|
-drawWidth / 2,
|
|
-drawHeight / 2,
|
|
drawWidth,
|
|
drawHeight
|
|
);
|
|
} else {
|
|
// Fallback: Zeichne einen einfachen Hintergrund mit Punkten
|
|
drawFallbackBackground();
|
|
}
|
|
|
|
// Restore context state
|
|
ctx.restore();
|
|
}
|
|
|
|
// Fallback-Hintergrund mit Punkten und Linien
|
|
function drawFallbackBackground() {
|
|
const width = canvas.width / (window.devicePixelRatio || 1);
|
|
const height = canvas.height / (window.devicePixelRatio || 1);
|
|
|
|
// Zeichne einige zufällige Punkte
|
|
ctx.fillStyle = isDarkMode ? 'rgba(139, 92, 246, 0.2)' : 'rgba(139, 92, 246, 0.1)';
|
|
|
|
for (let i = 0; i < 50; i++) {
|
|
const x = Math.random() * width;
|
|
const y = Math.random() * height;
|
|
const radius = Math.random() * 3 + 1;
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(x - width/2, y - height/2, radius, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
// Animation loop
|
|
function animate() {
|
|
// Update animation parameters
|
|
rotation += animationSpeed;
|
|
|
|
// Update scale with oscillation
|
|
scale += scaleSpeed * scaleDirection;
|
|
if (scale > 1.05) { // Kleinerer Skalierungsbereich für weniger starke Größenänderung
|
|
scaleDirection = -1;
|
|
} else if (scale < 0.95) {
|
|
scaleDirection = 1;
|
|
}
|
|
|
|
// Update opacity with oscillation
|
|
opacity += opacitySpeed * opacityDirection;
|
|
if (opacity > 0.75) { // Kleinerer Opazitätsbereich für subtilere Änderungen
|
|
opacityDirection = -1;
|
|
} else if (opacity < 0.65) {
|
|
opacityDirection = 1;
|
|
}
|
|
|
|
// Draw the image
|
|
drawNetworkImage();
|
|
|
|
// Request next frame
|
|
animationFrameId = requestAnimationFrame(animate);
|
|
}
|
|
|
|
// Cleanup Funktion für Speicherbereinigung
|
|
function cleanupNetworkBackground() {
|
|
if (animationFrameId) {
|
|
cancelAnimationFrame(animationFrameId);
|
|
animationFrameId = null;
|
|
}
|
|
|
|
if (canvas && canvas.parentNode) {
|
|
canvas.parentNode.removeChild(canvas);
|
|
}
|
|
|
|
window.removeEventListener('resize', resizeCanvas);
|
|
}
|
|
|
|
// Führe Initialisierung aus, wenn DOM geladen ist
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initNetworkBackground);
|
|
} else {
|
|
initNetworkBackground();
|
|
}
|
|
|
|
// Führe Cleanup durch, wenn das Fenster geschlossen wird
|
|
window.addEventListener('beforeunload', cleanupNetworkBackground);
|