How To Use

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

🎨 How to Use Flash Animations

Complete guide to implementing beautiful animations in your GoGrab Space applications.


🏗️ Architecture Overview

Flash-Animations provides a three-layer animation system combining Angular's native animations with Flash-Persistence for optimal performance and user customization:

┌──────────────────────────────────────────────┐
│  Layer 1: Route Configuration                │
│  Each route defines animation in route.data  │
│  Example: data: { animation: 'slideLeft' }   │
└────────────────┬─────────────────────────────┘
                 ↓
┌──────────────────────────────────────────────┐
│  Layer 2: FlashRouteAnimationService          │
│  - Tracks route changes automatically         │
│  - Uses Angular @animations API               │
│  - Provides current state as signals          │
└────────────────┬─────────────────────────────┘
                 ↓
┌──────────────────────────────────────────────┐
│  Layer 3: Flash-Persistence Integration      │
│  - Auto-saves user animation preferences      │
│  - Can override route animations              │
│  - Instant restoration on page reload         │
└──────────────────────────────────────────────┘

Why Angular Native Animations?

✅ Browser Caching - Animation definitions cached by Angular compiler
✅ AOT Compilation - Compiled ahead-of-time for instant execution
✅ Zero Dependencies - Uses built-in @angular/animations
✅ GPU Accelerated - Automatic transform/opacity optimization
✅ Lifecycle Integrated - Automatic cleanup and memory management
✅ Performance - Faster than CSS animations or third-party libraries

Priority System:

  1. User Preference (if overrideRouteAnimations: true in Flash-Persistence)
  2. Route Configuration (from route.data.animation)
  3. Default ('elegant')

📦 Installation

The library is already available in the monorepo. No installation needed!

Import path: @widgets-editor/flash-animations


🚀 Quick Start

1. Basic HTML Directive Usage

The simplest way to add animations to any element:

<!-- Fade in animation on mount -->
<div flashAnim="fade-in" faSpeed="fast">
  Content appears with fade effect
</div>

<!-- Slide up with elegant timing -->
<section flashAnim="slide-up" faSpeed="elegant">
  Beautiful entrance animation
</section>

2. Interactive Animations

Add animations triggered by user interactions:

<!-- Click animation -->
<button flashAnimClick="bounce-in">
  Click me!
</button>

<!-- Hover animation with exit effect -->
<div flashAnimHover="glow" flashAnimExit="fade-out">
  Hover to see effect
</div>

<!-- Multiple interactions -->
<button 
  flashAnimClick="shake" 
  flashAnimHover="pulse" 
  flashAnimFocus="scale-in">
  Interactive button
</button>

3. Scroll-Triggered Animations

Animate elements when they scroll into view:

<!-- Basic viewport animation -->
<div flashAnimViewport="slide-up" [flashAnimThreshold]="0.5">
  Appears when scrolled into view
</div>

<!-- With custom settings -->
<section 
  flashAnimViewport="fade-in"
  [flashAnimThreshold]="0.3"
  [faTriggerOnce]="true"
  faSpeed="elegant">
  Animates once on scroll
</section>

🎭 Available Animation Presets

Entrance Animations

Exit Animations

Attention Animations

Special Animations


⚡ Speed Settings

Control animation timing with speed options:

<div flashAnim="fade-in" faSpeed="instant">Quick fade</div>
<div flashAnim="slide-up" faSpeed="elegant">Smooth slide</div>

🎯 Directive Reference

FlashAnimateDirective - Basic Animations

Selector: [flashAnim]

<div 
  [flashAnim]="animationPreset"
  [faSpeed]="speed"
  [faDelay]="delayMs"
  [faDuration]="durationMs"
  [faEnabled]="isEnabled"
  [faTrigger]="triggerValue">
  Content
</div>

Inputs:

FlashAnimateInteractionDirective - User Interactions

Selector: [flashAnimClick], [flashAnimHover], [flashAnimFocus]

<button
  flashAnimClick="bounce-in"
  flashAnimHover="pulse"
  flashAnimFocus="glow"
  flashAnimBlur="fade-out"
  [faSpeed]="speed"
  [faEnabled]="isEnabled">
  Interactive element
</button>

Inputs:

FlashAnimateViewportDirective - Scroll Animations

Selector: [flashAnimViewport]

<div
  flashAnimViewport="slide-up"
  [flashAnimThreshold]="0.5"
  [faRootMargin]="'0px'"
  [faTriggerOnce]="true"
  [faSpeed]="speed"
  [faEnabled]="isEnabled"
  (faEnter)="onEnterViewport()"
  (faExit)="onExitViewport()">
  Viewport-aware content
</div>

Inputs:

Outputs:


🛣️ Route Animations (Recommended for Page Transitions)

Use Angular's native animations for smooth, cached, GPU-accelerated route transitions.

Complete Implementation

Step 1: App Component Setup

import { Component, OnInit, OnDestroy, signal } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
import { 
  flashRouteAnimations, 
  FlashRouteAnimationService 
} from '@widgets-editor/flash-animations';
import { storageKeyResolver } from '@flash-persistence';

@Component({
  selector: 'app-root',
  standalone: true,
  animations: [flashRouteAnimations],  // Angular native animations
  template: `
    <div class="route-container" 
         [@flashRouteTransition]="getRouteAnimationData()">
      <router-outlet></router-outlet>
    </div>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
  private animationDataCache = signal<string>('elegant');
  private storageUnsubscribe?: () => void;

  constructor(
    public routeAnimations: FlashRouteAnimationService,
    private router: Router
  ) {}

  ngOnInit() {
    // Initialize route animation tracking
    this.routeAnimations.initialize();

    // Update on route changes
    this.router.events
      .pipe(filter(event => event instanceof NavigationEnd))
      .subscribe(() => this.refreshAnimationCache());

    // Listen to Flash-Persistence animation preference changes
    this.storageUnsubscribe = storageKeyResolver.onStorageChange(
      'flash:animation-settings',
      () => this.refreshAnimationCache()
    );

    // Initial cache
    this.refreshAnimationCache();
  }

  getRouteAnimationData() {
    return this.animationDataCache();
  }

  private refreshAnimationCache() {
    // Get animation from route config or user preference
    const routeAnimation = this.routeAnimations.getCurrentAnimationState();
    this.animationDataCache.set(routeAnimation);
  }

  ngOnDestroy() {
    if (this.storageUnsubscribe) {
      this.storageUnsubscribe();
    }
  }
}

Step 2: Add GPU-Accelerated CSS

// app.component.scss
.route-container {
  position: relative;
  width: 100%;
  height: 100%;
  display: block;

  // GPU acceleration for smooth animations
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
  perspective: 1000px;

  // Smooth child transitions
  > * {
    will-change: transform, opacity;
    backface-visibility: hidden;
  }
}

Step 3: Enable Animations in main.ts

import { provideAnimations } from '@angular/platform-browser/animations';

export const appConfig: ApplicationConfig = {
  providers: [
    provideAnimations(),  // Synchronous loading (NOT provideAnimationsAsync)
    // ... other providers
  ]
};

⚠️ Important: Use provideAnimations(), not provideAnimationsAsync(), to ensure animations load immediately.

Step 4: Configure Route Animations

import { Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    data: {
      animation: 'elegant',           // Polished, subtle
      animationSpeed: 'normal'
    }
  },
  {
    path: 'space-map',
    component: SpaceMapComponent,
    data: {
      animation: 'scale',             // Zoom effect
      animationSpeed: 'elegant'
    }
  },
  {
    path: 'editor',
    component: EditorComponent,
    data: {
      animation: 'slideLeft',         // Directional
      animationSpeed: 'fast'
    }
  }
];

Available Route Animations

Animation Description Best For
elegant Polished, subtle transition Main areas, default
fade Simple opacity Quick changes
slideUp Slide from bottom Detail views
slideLeft Slide from right Forward navigation
slideRight Slide from left Back navigation
scale Zoom effect Focus transitions
bounce Playful bounce Fun interactions

Animation Speeds

Speed Duration Use Case
instant 100ms Data updates
fast 300ms Quick transitions
normal 600ms Standard (default)
slow 1200ms Dramatic
elegant 800ms Polished

Route Transition Flow

User clicks link
   ↓
Router navigates
   ↓
FlashRouteAnimationService reads route.data.animation
   ↓
Service updates currentAnimation signal
   ↓
App component receives new state
   ↓
[@flashRouteTransition] trigger fires
   ↓
:leave animation (200ms) - old view
   ↓
:enter animation (400ms) - new view
   ↓
Complete (total ~600ms)

Flash-Persistence Integration

Users can customize route animations in the /flash-persistence demo:

// In flash-animations-demo component:
protected readonly animationSettings = autoFlashState('animation-settings', {
  selectedPreset: 'slide-up',
  speed: 'fast',
  enabled: true,
  overrideRouteAnimations: true  // User preference takes priority
});

Priority:

  1. User preference (if overrideRouteAnimations: true)
  2. Route configuration (from route.data.animation)
  3. Default ('elegant')

💻 TypeScript Programmatic Control

Using Helper Functions

Quick and easy animation helpers:

import {
  flashAnimate,
  flashSuccess,
  flashError,
  flashPulse,
  flashShake,
  flashGlow
} from '@widgets-editor/flash-animations';

export class MyComponent {
  @ViewChild('element') element!: ElementRef;

  async animateElement() {
    // Simple animation
    await flashAnimate(this.element, 'fade-in', 'fast');
  }

  showSuccessMessage() {
    flashSuccess(this.element, 'Saved successfully!');
  }

  showError() {
    flashError(this.element, 'Something went wrong');
  }

  drawAttention() {
    flashPulse(this.element, 2); // Pulse twice
  }

  highlightElement() {
    flashGlow(this.element, '#22c55e', 800); // Green glow for 800ms
  }
}

Using FlashAnimationController

Advanced control with sequences and chains:

import { FlashAnimationController } from '@widgets-editor/flash-animations';

export class MyComponent {
  private controller = inject(FlashAnimationController);
  @ViewChild('element') element!: ElementRef;

  async complexAnimation() {
    // Single animation with options
    await this.controller.animate(this.element, 'bounce-in', {
      speed: 'fast',
      delay: 300,
      onComplete: () => console.log('Done!')
    });
  }

  async runSequence() {
    // Run animations in sequence
    await this.controller.sequence(this.element, [
      { preset: 'fade-in', speed: 'fast' },
      { preset: 'glow', duration: 800, delay: 200 },
      { preset: 'pulse', speed: 'normal' }
    ]);
  }

  async staggerAnimation() {
    // Animate multiple elements with stagger
    await this.controller.stagger(
      [this.card1, this.card2, this.card3],
      'slide-up',
      { speed: 'fast', staggerDelay: 150 }
    );
  }
}

Animation Chains (Fluent API)

Build complex animation sequences with a fluent API:

import { createAnimationChain } from '@widgets-editor/flash-animations';

export class MyComponent {
  @ViewChild('element') element!: ElementRef;

  async playAnimationChain() {
    const chain = createAnimationChain(this.element)
      .fadeIn('fast')
      .wait(300)
      .pulse(2)
      .glow(1000)
      .bounceIn('elegant');

    await chain.play();
  }
}

🎨 Advanced Examples

Example 1: Animated Card Grid

<div class="card-grid">
  @for (card of cards; track card.id; let i = $index) {
    <div 
      class="card"
      flashAnimViewport="slide-up"
      [flashAnimThreshold]="0.3"
      [faDelay]="i * 100"
      faSpeed="elegant">
      <h3>{{ card.title }}</h3>
      <p>{{ card.description }}</p>
      <button flashAnimClick="bounce-in" flashAnimHover="pulse">
        Action
      </button>
    </div>
  }
</div>

Example 2: Form Validation Feedback

export class FormComponent {
  @ViewChild('emailInput') emailInput!: ElementRef;

  async validateEmail() {
    if (this.email.invalid) {
      await flashShake(this.emailInput);
      await flashError(this.emailInput);
    } else {
      await flashSuccess(this.emailInput);
    }
  }
}
<input 
  #emailInput
  type="email" 
  [(ngModel)]="email"
  (blur)="validateEmail()">

Example 3: Loading States

export class DataComponent {
  @ViewChild('container') container!: ElementRef;

  async loadData() {
    const stopLoading = flashStartLoading(this.container, 'Loading...');

    try {
      await this.dataService.fetchData();
      flashSuccess(this.container);
    } catch (error) {
      flashError(this.container);
    } finally {
      stopLoading();
    }
  }
}

Example 4: State Change Animations

export class CounterComponent {
  count = signal(0);
  
  @ViewChild('display') display!: ElementRef;

  async increment() {
    this.count.update(c => c + 1);
    await flashPulse(this.display);
  }
}
<div #display [flashAnim]="'pulse'" [faTrigger]="count()">
  Count: {{ count() }}
</div>

♿ Accessibility

Flash Animations automatically respects user preferences:

Prefers Reduced Motion

The library automatically detects prefers-reduced-motion and:

Keyboard Navigation

All interactive directives support keyboard navigation:

Screen Readers

Animations don't interfere with screen readers and maintain semantic HTML structure.


🔧 Configuration & Customization

Component-Level Setup

import {
  FlashAnimateDirective,
  FlashAnimateInteractionDirective,
  FlashAnimateViewportDirective
} from '@widgets-editor/flash-animations';

@Component({
  standalone: true,
  imports: [
    FlashAnimateDirective,
    FlashAnimateInteractionDirective,
    FlashAnimateViewportDirective
  ]
})
export class MyComponent {}

Import Types

import {
  FlashAnimationPreset,
  FlashAnimationSpeed,
  FlashAnimationOptions
} from '@widgets-editor/flash-animations';

🎯 Best Practices

1. Use Appropriate Animations

2. Choose Right Speed

3. Respect User Preferences

4. Performance Tips

5. Accessibility


🐛 Troubleshooting

Animations Not Working?

  1. Check imports:

    imports: [FlashAnimateDirective, ...]
    
  2. Rebuild library:

    yarn nx build flash-animations
    
  3. Restart dev server:

    nx serve your-app
    

Type Errors?

Ensure you import types:

import { FlashAnimationPreset } from '@widgets-editor/flash-animations';

Route Animations Not Triggering?

Make sure FlashRouteAnimationService is initialized:

ngOnInit() {
  this.routeAnimations.initialize();
}

📚 Additional Resources


🎉 Summary

Flash Animations provides three simple ways to add animations:

  1. HTML Directives - flashAnim, flashAnimClick, flashAnimViewport
  2. Helper Functions - flashAnimate(), flashSuccess(), etc.
  3. Route Animations - flashRouteAnimations service

Choose the method that best fits your use case and enjoy beautiful, performant animations throughout your application!


🌠 Happy Animating!