Compare commits
2 Commits
32ecfcb621
...
96d4659652
| Author | SHA1 | Date | |
|---|---|---|---|
| 96d4659652 | |||
| ad43459173 |
@@ -1,6 +1,6 @@
|
||||
<app-particles-background></app-particles-background>
|
||||
<app-topbar />
|
||||
|
||||
<main class="algo-container app-surface">
|
||||
<main class="app-container app-surface">
|
||||
<router-outlet />
|
||||
</main>
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import {TopbarComponent} from '../topbar/topbar.component';
|
||||
import {TranslatePipe} from '@ngx-translate/core';
|
||||
import {ParticleBackgroundComponent} from '../../shared/components/particles-background/particles-background.component';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, TopbarComponent, TranslatePipe],
|
||||
imports: [RouterOutlet, TopbarComponent, TranslatePipe, ParticleBackgroundComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<canvas #canvas></canvas>
|
||||
@@ -0,0 +1,14 @@
|
||||
:host {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import {AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, ViewChild} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-particles-background',
|
||||
imports: [],
|
||||
templateUrl: './particles-background.component.html',
|
||||
styleUrl: './particles-background.component.scss',
|
||||
})
|
||||
export class ParticleBackgroundComponent implements AfterViewInit, OnDestroy {
|
||||
@ViewChild('canvas', { static: true }) canvasRef!: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
private ctx!: CanvasRenderingContext2D;
|
||||
private particles: any[] = [];
|
||||
private animationFrameId: number = 0;
|
||||
|
||||
// --- Configuration ---
|
||||
private readonly numParticles = 80;
|
||||
private readonly maxDistance = 150;
|
||||
private readonly particleSpeed = 0.8;
|
||||
|
||||
constructor(private ngZone: NgZone) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const canvas = this.canvasRef.nativeElement;
|
||||
this.ctx = canvas.getContext('2d')!;
|
||||
|
||||
this.resizeCanvas();
|
||||
this.initParticles();
|
||||
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
this.animate();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
}
|
||||
|
||||
@HostListener('window:resize')
|
||||
resizeCanvas(): void {
|
||||
const canvas = this.canvasRef.nativeElement;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
}
|
||||
|
||||
private initParticles(): void {
|
||||
this.particles = [];
|
||||
const canvas = this.canvasRef.nativeElement;
|
||||
|
||||
for (let i = 0; i < this.numParticles; i++) {
|
||||
this.particles.push({
|
||||
x: Math.random() * canvas.width,
|
||||
y: Math.random() * canvas.height,
|
||||
vx: (Math.random() - 0.5) * this.particleSpeed,
|
||||
vy: (Math.random() - 0.5) * this.particleSpeed,
|
||||
radius: Math.random() * 1.5 + 0.5
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private animate = (): void => {
|
||||
const canvas = this.canvasRef.nativeElement;
|
||||
|
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for (let i = 0; i < this.numParticles; i++) {
|
||||
let p = this.particles[i];
|
||||
|
||||
p.x += p.vx;
|
||||
p.y += p.vy;
|
||||
|
||||
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
|
||||
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
|
||||
this.ctx.fillStyle = 'rgba(120, 150, 170, 0.4)';
|
||||
this.ctx.fill();
|
||||
|
||||
for (let j = i + 1; j < this.numParticles; j++) {
|
||||
let p2 = this.particles[j];
|
||||
|
||||
let dx = p.x - p2.x;
|
||||
let dy = p.y - p2.y;
|
||||
let distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < this.maxDistance) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(p.x, p.y);
|
||||
this.ctx.lineTo(p2.x, p2.y);
|
||||
|
||||
let opacity = (1 - (distance / this.maxDistance)) * 0.5;
|
||||
this.ctx.strokeStyle = `rgba(120, 150, 170, ${opacity})`;
|
||||
this.ctx.lineWidth = 1;
|
||||
this.ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.animationFrameId = requestAnimationFrame(this.animate);
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<p>particles-background works!</p>
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-particles-background',
|
||||
imports: [],
|
||||
templateUrl: './particles-background.html',
|
||||
styleUrl: './particles-background.scss',
|
||||
})
|
||||
export class ParticlesBackground {
|
||||
|
||||
}
|
||||
@@ -407,9 +407,15 @@ app-root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
width: 100%;
|
||||
max-width: var(--app-maxWidth);
|
||||
margin: 1rem auto;
|
||||
|
||||
}
|
||||
|
||||
.app-surface {
|
||||
flex-grow: 1;
|
||||
background: var(--app-bg);
|
||||
color: var(--app-fg);
|
||||
transition: background-color 220ms ease, color 220ms ease;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user