Hot To Use

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

📚 Scroll Trigger - Complete Usage Guide

Everything you need to create stunning scroll animations in your Angular application.


🚀 Installation

1. Import the Directive

import { EnhancedScrollTriggerDirective } from '@widgets-editor/scroll-trigger';

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [EnhancedScrollTriggerDirective],
  template: `...`
})
export class MyComponent {}

🎯 Basic Usage

Example 1: Simple Fade-In

<div enhancedScrollTrigger="fade-section"
     [animationPreset]="'fade-in'">
  <h2>This fades in when scrolled into view!</h2>
</div>

Example 2: Custom Trigger Positions

<div enhancedScrollTrigger="hero-title"
     [animationPreset]="'slide-up'"
     [start]="'top 80%'"
     [end]="'top 20%'">
  <h1>Animated Hero Title</h1>
</div>

Example 3: With Visual Triggers

<div enhancedScrollTrigger="product-card"
     [animationPreset]="'explode'"
     [showTriggers]="true"
     [triggerColor]="'#22c55e'">
  <div class="product">Product Card</div>
</div>

🎨 Animation Presets

Available Presets

Preset Effect Best For
fade-in Gradual opacity increase Subtle reveals
slide-up Upward motion + fade Text content
slide-down Downward motion + fade Headers
slide-left Left motion + fade Side content
slide-right Right motion + fade Side content
scale-in Grow from center Images, cards
scale-out Shrink to center Dismissals
explode Dramatic expansion Hero sections
vortex Spinning rotation Eye-catching reveals
morph Shape transformation Creative effects
magnetize Pull-in effect Interactive elements
spiral Spiral rotation Decorative elements

Usage

// Simple preset
<div [animationPreset]="'fade-in'"></div>

// With speed control
<div [animationPreset]="'explode'"
     [animationSpeed]="'fast'"></div>

// With delay
<div [animationPreset]="'vortex'"
     [animationDelay]="500"></div>

🎯 Trigger Position Syntax

Keyword Positions

// Element position keywords: 'top' | 'center' | 'bottom'
// Viewport position keywords: 'top' | 'center' | 'bottom'

[start]="'top bottom'"      // Element top hits viewport bottom
[start]="'center center'"   // Element center aligns with viewport center
[start]="'bottom top'"      // Element bottom reaches viewport top

Percentage Offsets

// Progress percentage for end trigger
[start]="'top bottom'"
[end]="'80%'"               // Animation completes at 80% of distance

// Viewport percentage for start trigger
[start]="'top 20%'"         // Trigger when element hits 20% from top
[end]="'bottom 80%'"        // End when element bottom hits 80%

Pixel Offsets

[start]="'100px bottom'"    // 100px offset from calculated position
[end]="'200px'"             // 200px offset

Combined Syntax

// Element keyword + Viewport percentage
[start]="'top 25%'"
[end]="'bottom 75%'"

// With pixel offset
[start]="'center 50%'"
[end]="'100px'"

✏️ Visual Editor

Enable Visual Triggers

<div enhancedScrollTrigger="my-element"
     [showTriggers]="true"
     [triggerColor]="'#3b82f6'">
  Content
</div>

Enable Edit Mode

<div enhancedScrollTrigger="my-element"
     [showTriggers]="true"
     [editMode]="true"
     [triggerColor]="'#22c55e'">
  Click me to open the editor!
  Drag the trigger lines to adjust timing!
</div>

Global Control Example

@Component({
  template: `
    <button (click)="showTriggers.set(!showTriggers())">
      {{ showTriggers() ? 'Hide' : 'Show' }} Triggers
    </button>
    
    <button (click)="editMode.set(!editMode())">
      {{ editMode() ? 'Exit' : 'Enter' }} Edit Mode
    </button>
    
    <div enhancedScrollTrigger="elem-1"
         [showTriggers]="showTriggers()"
         [editMode]="editMode()"
         [triggerColor]="'#22c55e'">
      Element 1
    </div>
  `
})
export class MyComponent {
  protected showTriggers = signal(false);
  protected editMode = signal(false);
}

🎬 Multi-Step Animations

Configuration Structure

import { ScrollAnimationConfig } from '@widgets-editor/scroll-trigger';

const config: ScrollAnimationConfig = {
  id: 'complex-card',
  animationPreset: 'fade-in',
  startTrigger: 'top bottom',
  endTrigger: '90%',
  exitBehavior: 'none',
  scrubbing: false,
  pinning: false,
  steps: [
    {
      id: 'step-1',
      animationPreset: 'fade-in',
      delay: 0,
      duration: 300,
      easing: 'ease-out',
      parallel: false
    },
    {
      id: 'step-2',
      animationPreset: 'slide-up',
      delay: 100,
      duration: 400,
      easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
      parallel: true  // Runs WITH step-1
    },
    {
      id: 'step-3',
      animationPreset: 'scale-in',
      delay: 0,
      duration: 500,
      easing: 'ease-in-out',
      parallel: false  // Runs AFTER step-2
    }
  ]
};

Visual Editor UI

  1. Click element to open editor
  2. Navigate to "Multi-Step" tab
  3. Click "➕ Add Keyframe"
  4. Configure each step:
    • Animation preset
    • Delay (ms)
    • Duration (ms)
    • Easing function
    • Parallel execution
  5. Save - automatically persists!

💾 Automatic Persistence

Using Flash Persistence

import { autoFlashState } from '@flash-persistence';

@Component({
  template: `
    @for (element of elements(); track element.id) {
      <div [enhancedScrollTrigger]="element.id"
           [animationPreset]="element.animation"
           [start]="element.start"
           [end]="element.end">
        {{ element.label }}
      </div>
    }
  `
})
export class MyComponent {
  // ✨ Automatically persists across page refreshes!
  protected elements = autoFlashState('my-scroll-elements', [
    {
      id: 'hero-1',
      label: 'Hero Section',
      animation: 'fade-in' as FlashAnimationPreset,
      start: 'top bottom',
      end: '80%'
    }
  ]);
  
  // Update any element - automatically saved!
  updateElement(id: string, config: Partial<ElementConfig>) {
    this.elements.update(els =>
      els.map(el => el.id === id ? { ...el, ...config } : el)
    );
    // No manual save() needed! ✨
  }
}

🎮 Complete API Reference

Directive Inputs

Input Type Default Description
enhancedScrollTrigger string required Unique element ID
start string 'top bottom' When animation starts
end string '80%' When animation completes
animationPreset FlashAnimationPreset undefined Animation type
animationSpeed 'instant' | 'fast' | 'elegant' | 'smooth' | 'slow' 'elegant' Animation speed
animationDelay number 0 Delay before animation (ms)
scrub boolean | number false Smooth scrubbing
pin boolean false Pin element during animation
showTriggers boolean false Show visual trigger guides
triggerColor string undefined Custom trigger color
editMode boolean false Enable draggable triggers
debug boolean false Enable verbose logging

Directive Outputs

Output Type Description
onEnter EventEmitter<ScrollTriggerEvent> Fires when entering trigger zone
onLeave EventEmitter<ScrollTriggerEvent> Fires when leaving trigger zone
onUpdate EventEmitter<ScrollTriggerEvent> Fires on scroll (if scrubbing enabled)
onToggle EventEmitter<ScrollTriggerEvent> Fires on enter/leave toggle

Event Data Structure

interface ScrollTriggerEvent {
  direction: 'forward' | 'reverse';
  progress: number;        // 0-1
  velocity: number;        // Scroll speed
  isActive: boolean;       // Currently in trigger zone?
}

📋 Common Recipes

Recipe 1: Staggered List Animations

@Component({
  template: `
    @for (item of items; track item.id; let i = $index) {
      <div [enhancedScrollTrigger]="'item-' + i"
           [animationPreset]="'slide-up'"
           [animationDelay]="i * 100"
           [start]="'top 90%'"
           [end]="'top 60%'">
        {{ item.title }}
      </div>
    }
  `
})

Recipe 2: Scroll-Linked Progress Bar

@Component({
  template: `
    <div enhancedScrollTrigger="progress-section"
         [start]="'top bottom'"
         [end]="'bottom top'"
         [scrub]="true"
         (onUpdate)="updateProgress($event)">
      <div class="progress-bar" [style.width.%]="progress()"></div>
    </div>
  `
})
export class MyComponent {
  protected progress = signal(0);
  
  updateProgress(event: ScrollTriggerEvent) {
    this.progress.set(event.progress * 100);
  }
}

Recipe 3: Pinned Section with Animation

<div enhancedScrollTrigger="pinned-hero"
     [animationPreset]="'scale-in'"
     [pin]="true"
     [start]="'top top'"
     [end]="'+=500'">
  <h1>This stays pinned while animating!</h1>
</div>

Recipe 4: Conditional Animations

<div [enhancedScrollTrigger]="'conditional-elem'"
     [animationPreset]="isDesktop() ? 'explode' : 'fade-in'"
     [start]="isDesktop() ? 'top 80%' : 'top 90%'">
  Different animations for different devices!
</div>

🎯 Performance Tips

1. Use Unique IDs

// ✅ Good
enhancedScrollTrigger="hero-section-title"
enhancedScrollTrigger="product-card-3"

// ❌ Bad
enhancedScrollTrigger="div"
enhancedScrollTrigger="element"

2. Disable Triggers in Production

<div [enhancedScrollTrigger]="'my-elem'"
     [showTriggers]="isDevMode()"
     [editMode]="false">

3. Use Simple Trigger Positions

// ✅ Fast calculation
[start]="'top bottom'"
[end]="'80%'"

// ⚠️ Slower (but still fast)
[start]="'center 37.5%'"
[end]="'bottom 87.23%'"

4. Group Similar Animations

// Instead of 10 individual configs, use one shared config
const sharedConfig = {
  start: 'top bottom',
  end: '80%',
  animation: 'fade-in'
};

🐛 Troubleshooting

Issue: Animations not triggering

Solution: Ensure element has content/height and is scrollable into view.

// ❌ Problem: No height
<div enhancedScrollTrigger="empty"></div>

// ✅ Solution: Has content
<div enhancedScrollTrigger="with-content">
  <h2>Content Here</h2>
</div>

Issue: Triggers not visible

Solution: Check showTriggers and triggerColor inputs.

// Make sure these are set
[showTriggers]="true"
[triggerColor]="'#22c55e'"  // Any valid CSS color

Issue: Persistence not working

Solution: Ensure Flash Persistence is configured in main.ts:

import { provideAutoFlashPersistence } from '@flash-persistence';

bootstrapApplication(AppComponent, {
  providers: [
    provideAutoFlashPersistence({
      namespace: 'my-app'
    })
  ]
});

🚀 Next Steps


Ready to build? Start scrolling! 🎢