diff --git a/src/app/app.html b/src/app/app.html index 7dd570e..e5f86a8 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1 +1,2 @@ - + + \ No newline at end of file diff --git a/src/app/app.ts b/src/app/app.ts index 2c33e2d..32569c1 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { ThemeSwitchService } from './core/service/theme-switch.service'; +import { Background } from "./core/components/background/background"; @Component({ selector: 'app-root', - imports: [RouterOutlet], + imports: [RouterOutlet, Background], templateUrl: './app.html', }) export class App { diff --git a/src/app/core/components/background/background.html b/src/app/core/components/background/background.html new file mode 100644 index 0000000..e0bd711 --- /dev/null +++ b/src/app/core/components/background/background.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/core/components/background/background.scss b/src/app/core/components/background/background.scss new file mode 100644 index 0000000..08d0458 --- /dev/null +++ b/src/app/core/components/background/background.scss @@ -0,0 +1,8 @@ +canvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; +} \ No newline at end of file diff --git a/src/app/core/components/background/background.ts b/src/app/core/components/background/background.ts new file mode 100644 index 0000000..66a566b --- /dev/null +++ b/src/app/core/components/background/background.ts @@ -0,0 +1,108 @@ +import { Component, ElementRef, HostListener, ViewChild } from '@angular/core'; + +@Component({ + selector: 'app-background', + imports: [], + templateUrl: './background.html', + styleUrl: './background.scss' +}) +export class Background { + + @ViewChild('canvas', { static: true }) canvasRef!: ElementRef; + + // Config + private numStars = 150; + private minBlink = 0.2; + private maxBlink = 1; + private blinkSpeed = 0.002; // Stern blinkt langsamer oder schneller + private maxSpeed = 0.1; // Sternbewegungsgeschwindigkeit + private parallaxFactor = 0.5; // Scroll-Parallax-Faktor + + // Feature toggles + private enableBlink = true; + private enableMovement = true; + + private ctx!: CanvasRenderingContext2D; + private stars: Star[] = []; + private width = window.innerWidth; + private height = window.innerHeight; + private animationId!: number; + private scrollY = window.scrollY; + + ngOnInit() { + const canvas = this.canvasRef.nativeElement; + this.ctx = canvas.getContext('2d')!; + canvas.width = this.width; + canvas.height = this.height; + + for (let i = 0; i < (this.numStars) * (document.body.scrollHeight / this.height); i++) { + this.stars.push({ + x: Math.random() * this.width, + y: Math.random() * document.body.scrollHeight, + radius: Math.random() * 1.5 + 0.5, + alpha: this.minBlink + Math.random() * (this.maxBlink - this.minBlink), + alphaChange: (Math.random() * this.blinkSpeed) + 0.0005, + vx: (Math.random() - 0.5) * this.maxSpeed, + vy: (Math.random() - 0.5) * this.maxSpeed + }); + } + + this.animate(); + } + + @HostListener('window:resize') + onResize() { + this.width = window.innerWidth; + this.height = window.innerHeight; + const canvas = this.canvasRef.nativeElement; + canvas.width = this.width; + canvas.height = this.height; + } + + @HostListener('window:scroll') + onScroll() { + this.scrollY = window.scrollY; + } + + private animate() { + const ctx = this.ctx; + ctx.clearRect(0, 0, this.width, this.height); + + for (let star of this.stars) { + // Blinken + if (this.enableBlink) { + star.alpha += star.alphaChange; + if (star.alpha > this.maxBlink || star.alpha < this.minBlink) star.alphaChange *= -1; + } + + // Bewegung + if (this.enableMovement) { + star.x += star.vx; + star.y += star.vy; + } + + // Stern zeichnen mit Scroll-Parallax + ctx.beginPath(); + ctx.arc(star.x, star.y - this.scrollY * this.parallaxFactor, star.radius, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 215, 0, ${star.alpha})`; // gold + ctx.fill(); + } + + this.animationId = requestAnimationFrame(() => this.animate()); + } + + ngOnDestroy() { + cancelAnimationFrame(this.animationId); + } + +} + +interface Star { + x: number; + y: number; + radius: number; + alpha: number; + alphaChange: number; + vx: number; + vy: number; +} diff --git a/src/styles.scss b/src/styles.scss index 31340f5..b94a413 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -4,6 +4,8 @@ body { font-family: 'Open Sans Variable', sans-serif; + width: 100vw; + min-height: 100vh; margin: 0; padding: 15px; font-size: 14px; diff --git a/tsconfig.json b/tsconfig.json index 769b80e..e2047cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, - "isolatedModules": false, + "isolatedModules": true, "experimentalDecorators": true, "importHelpers": true, "target": "ES2022",