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:
@@ -0,0 +1,99 @@
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'fl-ambient-light',
|
||||
standalone: true,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div class="fl-ambient-light__layer-1" [style.background]="layer1()"></div>
|
||||
<div class="fl-ambient-light__layer-2" [style.background]="layer2()"></div>
|
||||
<div class="fl-ambient-light__layer-3" [style.background]="layer3()"></div>
|
||||
`,
|
||||
styleUrl: './fl-ambient-light.component.scss',
|
||||
host: {
|
||||
'class': 'fl-ambient-light',
|
||||
},
|
||||
})
|
||||
export class FlAmbientLightComponent {
|
||||
private el = inject(ElementRef);
|
||||
private reducedMotion = inject(ReducedMotionService);
|
||||
private animationLoop = inject(AnimationLoopService);
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
// Inputs
|
||||
readonly colors = input<string[]>(['#3b82f6', '#8b5cf6', '#ec4899']);
|
||||
readonly speed = input(1);
|
||||
readonly blur = input(100);
|
||||
readonly opacity = input(0.5);
|
||||
|
||||
// Internal
|
||||
private elapsed = 0;
|
||||
private loopCleanup: (() => void) | null = null;
|
||||
|
||||
// Signals for layer backgrounds
|
||||
readonly layer1 = signal('');
|
||||
readonly layer2 = signal('');
|
||||
readonly layer3 = signal('');
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => {
|
||||
if (this.reducedMotion.reduced()) {
|
||||
this.setStaticGradients();
|
||||
return;
|
||||
}
|
||||
|
||||
const id = `ambient-light-${Math.random().toString(36).slice(2)}`;
|
||||
this.loopCleanup = this.animationLoop.register(id, (delta) => this.tick(delta));
|
||||
});
|
||||
|
||||
this.destroyRef.onDestroy(() => {
|
||||
this.loopCleanup?.();
|
||||
});
|
||||
}
|
||||
|
||||
private tick(delta: number): void {
|
||||
this.elapsed += delta * this.speed();
|
||||
this.updateGradients();
|
||||
}
|
||||
|
||||
private updateGradients(): void {
|
||||
const host = this.el.nativeElement as HTMLElement;
|
||||
const width = host.offsetWidth;
|
||||
const height = host.offsetHeight;
|
||||
const colors = this.colors();
|
||||
const blur = this.blur();
|
||||
const opacity = this.opacity();
|
||||
|
||||
// Layer 1
|
||||
const x1 = 50 + Math.sin(this.elapsed * 0.3) * 30;
|
||||
const y1 = 50 + Math.cos(this.elapsed * 0.2) * 30;
|
||||
const color1 = colors[0] || '#3b82f6';
|
||||
this.layer1.set(`radial-gradient(circle at ${x1}% ${y1}%, ${color1} 0%, transparent ${blur}%)`);
|
||||
|
||||
// Layer 2
|
||||
const x2 = 50 + Math.sin(this.elapsed * 0.4 + Math.PI) * 25;
|
||||
const y2 = 50 + Math.cos(this.elapsed * 0.3 + Math.PI) * 25;
|
||||
const color2 = colors[1] || '#8b5cf6';
|
||||
this.layer2.set(`radial-gradient(circle at ${x2}% ${y2}%, ${color2} 0%, transparent ${blur}%)`);
|
||||
|
||||
// Layer 3
|
||||
const x3 = 50 + Math.sin(this.elapsed * 0.5 + Math.PI * 0.5) * 20;
|
||||
const y3 = 50 + Math.cos(this.elapsed * 0.4 + Math.PI * 0.5) * 20;
|
||||
const color3 = colors[2] || '#ec4899';
|
||||
this.layer3.set(`radial-gradient(circle at ${x3}% ${y3}%, ${color3} 0%, transparent ${blur}%)`);
|
||||
}
|
||||
|
||||
private setStaticGradients(): void {
|
||||
const colors = this.colors();
|
||||
const blur = this.blur();
|
||||
|
||||
this.layer1.set(`radial-gradient(circle at 50% 50%, ${colors[0] || '#3b82f6'} 0%, transparent ${blur}%)`);
|
||||
this.layer2.set(`radial-gradient(circle at 30% 70%, ${colors[1] || '#8b5cf6'} 0%, transparent ${blur}%)`);
|
||||
this.layer3.set(`radial-gradient(circle at 70% 30%, ${colors[2] || '#ec4899'} 0%, transparent ${blur}%)`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user