feat: add @sda/flair-elements-ui library
Angular 19+ standalone library with 61 components, 15 directives, and 8 services for decorative visual effects, animations, and micro-interactions. Components include particle systems, flow fields, aurora effects, tilt/holographic/spotlight cards, text effects, dividers, scroll animations, generative art, celebrations, image treatments, morphing shapes, terminal output, and pattern textures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
121
src/components/fl-gradient-mesh/fl-gradient-mesh.component.ts
Normal file
121
src/components/fl-gradient-mesh/fl-gradient-mesh.component.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
Component, ChangeDetectionStrategy, input, inject,
|
||||
ElementRef, afterNextRender, DestroyRef, signal,
|
||||
} from '@angular/core';
|
||||
import { ReducedMotionService } from '../../services/reduced-motion.service';
|
||||
import { AnimationLoopService } from '../../services/animation-loop.service';
|
||||
|
||||
interface MeshPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
vx: number;
|
||||
vy: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'fl-gradient-mesh',
|
||||
standalone: true,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
@for (point of meshPoints(); track $index) {
|
||||
<div
|
||||
class="fl-gradient-mesh__blob"
|
||||
[style.left.%]="point.x"
|
||||
[style.top.%]="point.y"
|
||||
[style.background]="point.color"
|
||||
[style.filter]="'blur(' + blur() + 'px)'"
|
||||
></div>
|
||||
}
|
||||
`,
|
||||
styleUrl: './fl-gradient-mesh.component.scss',
|
||||
host: {
|
||||
'class': 'fl-gradient-mesh',
|
||||
},
|
||||
})
|
||||
export class FlGradientMeshComponent {
|
||||
private el = inject(ElementRef);
|
||||
private reducedMotion = inject(ReducedMotionService);
|
||||
private animationLoop = inject(AnimationLoopService);
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
// Inputs
|
||||
readonly points = input(5);
|
||||
readonly colors = input<string[]>(['#3b82f6', '#8b5cf6', '#ec4899', '#f59e0b']);
|
||||
readonly speed = input(1);
|
||||
readonly blur = input(80);
|
||||
|
||||
// Internal
|
||||
private meshPointsData: MeshPoint[] = [];
|
||||
private loopCleanup: (() => void) | null = null;
|
||||
|
||||
// Signals
|
||||
readonly meshPoints = signal<Array<{ x: number; y: number; color: string }>>([]);
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => {
|
||||
this.initializeMesh();
|
||||
|
||||
if (this.reducedMotion.reduced()) {
|
||||
this.updateDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
const id = `gradient-mesh-${Math.random().toString(36).slice(2)}`;
|
||||
this.loopCleanup = this.animationLoop.register(id, (delta) => this.tick(delta));
|
||||
});
|
||||
|
||||
this.destroyRef.onDestroy(() => {
|
||||
this.loopCleanup?.();
|
||||
});
|
||||
}
|
||||
|
||||
private initializeMesh(): void {
|
||||
const count = this.points();
|
||||
const colors = this.colors();
|
||||
this.meshPointsData = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.meshPointsData.push({
|
||||
x: Math.random() * 100,
|
||||
y: Math.random() * 100,
|
||||
vx: (Math.random() - 0.5) * 10,
|
||||
vy: (Math.random() - 0.5) * 10,
|
||||
color: colors[i % colors.length],
|
||||
});
|
||||
}
|
||||
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
private tick(delta: number): void {
|
||||
const speed = this.speed() * delta * 10;
|
||||
|
||||
for (const point of this.meshPointsData) {
|
||||
point.x += point.vx * speed;
|
||||
point.y += point.vy * speed;
|
||||
|
||||
// Bounce off edges
|
||||
if (point.x < 0 || point.x > 100) {
|
||||
point.vx *= -1;
|
||||
point.x = Math.max(0, Math.min(100, point.x));
|
||||
}
|
||||
if (point.y < 0 || point.y > 100) {
|
||||
point.vy *= -1;
|
||||
point.y = Math.max(0, Math.min(100, point.y));
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
private updateDisplay(): void {
|
||||
this.meshPoints.set(
|
||||
this.meshPointsData.map(p => ({
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
color: p.color,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user