or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animation-composition.mdindex.mdprogrammatic-control.mdreusable-animations.mdstyling-timing.mdtriggers.md
tile.json

animation-composition.mddocs/

Animation Composition

Advanced functions for creating complex animations by combining multiple animation steps in parallel or sequence, and for querying and manipulating child elements.

Capabilities

Group Function

Runs multiple animation steps in parallel (simultaneously).

/**
 * Defines animation steps to run in parallel
 * @param steps - Array of animation metadata to execute simultaneously
 * @param options - Optional timing and parameter configuration
 * @returns AnimationGroupMetadata for parallel execution
 */
function group(
  steps: AnimationMetadata[],
  options?: AnimationOptions | null
): AnimationGroupMetadata;

interface AnimationGroupMetadata extends AnimationMetadata {
  steps: AnimationMetadata[];
  options: AnimationOptions | null;
}

Usage Examples:

import { group, animate, style } from '@angular/animations';

// Animate multiple properties simultaneously
const parallelAnimation = group([
  animate('300ms', style({ opacity: 0 })),
  animate('300ms', style({ transform: 'translateX(100px)' })),
  animate('300ms', style({ backgroundColor: 'red' }))
]);

// Different timings in parallel
const staggeredGroup = group([
  animate('200ms', style({ opacity: 0 })),        // Fastest
  animate('400ms', style({ transform: 'scale(0)' })), // Medium
  animate('600ms', style({ color: 'blue' }))       // Slowest
]);

Sequence Function

Runs multiple animation steps one after another (sequentially).

/**
 * Defines animation steps to run in sequence
 * @param steps - Array of animation metadata to execute sequentially
 * @param options - Optional timing and parameter configuration
 * @returns AnimationSequenceMetadata for sequential execution
 */
function sequence(
  steps: AnimationMetadata[],
  options?: AnimationOptions | null
): AnimationSequenceMetadata;

interface AnimationSequenceMetadata extends AnimationMetadata {
  steps: AnimationMetadata[];
  options: AnimationOptions | null;
}

Usage Examples:

import { sequence, animate, style } from '@angular/animations';

// Multi-step sequential animation
const sequentialAnimation = sequence([
  animate('200ms', style({ transform: 'translateY(-10px)' })),  // Step 1
  animate('300ms', style({ opacity: 0.5 })),                   // Step 2
  animate('200ms', style({ transform: 'translateY(0)', opacity: 1 })) // Step 3
]);

// Complex entrance sequence
const entranceSequence = sequence([
  style({ opacity: 0, transform: 'scale(0.8)' }),              // Initial state
  animate('150ms ease-out', style({ opacity: 1 })),            // Fade in
  animate('200ms cubic-bezier(0.68, -0.55, 0.265, 1.55)', 
    style({ transform: 'scale(1)' }))                          // Scale with bounce
]);

Query Function

Finds inner elements and applies animations to them.

/**
 * Queries child elements and applies animations
 * @param selector - CSS selector to find child elements
 * @param animation - Animation to apply to found elements
 * @param options - Optional query configuration and timing
 * @returns AnimationQueryMetadata for child element animation
 */
function query(
  selector: string,
  animation: AnimationMetadata | AnimationMetadata[],
  options?: AnimationQueryOptions | null
): AnimationQueryMetadata;

interface AnimationQueryOptions extends AnimationOptions {
  optional?: boolean;
  limit?: number;
}

interface AnimationQueryMetadata extends AnimationMetadata {
  selector: string;
  animation: AnimationMetadata | AnimationMetadata[];
  options: AnimationQueryOptions | null;
}

Usage Examples:

import { query, animate, style, stagger } from '@angular/animations';

// Animate all child elements
const animateChildren = query('.child', [
  animate('300ms', style({ opacity: 0, transform: 'translateY(20px)' }))
]);

// Animate specific child elements with stagger
const staggeredChildren = query('.item', [
  style({ opacity: 0, transform: 'translateX(-50px)' }),
  stagger('100ms', [
    animate('300ms ease-out', style({ 
      opacity: 1, 
      transform: 'translateX(0)' 
    }))
  ])
]);

// Optional query (won't fail if elements not found)
const optionalQuery = query('.optional-element', [
  animate('200ms', style({ opacity: 0 }))
], { optional: true });

// Limited query (animate only first 3 elements)
const limitedQuery = query('.item', [
  animate('300ms', style({ backgroundColor: 'yellow' }))
], { limit: 3 });

Stagger Function

Staggers the timing of animations across multiple elements.

/**
 * Staggers animation timing for multiple elements
 * @param timings - Delay between each element's animation start
 * @param animation - Animation to apply with staggered timing
 * @returns AnimationStaggerMetadata for staggered execution
 */
function stagger(
  timings: string | number,
  animation: AnimationMetadata | AnimationMetadata[]
): AnimationStaggerMetadata;

interface AnimationStaggerMetadata extends AnimationMetadata {
  timings: string | number;
  animation: AnimationMetadata | AnimationMetadata[];
}

Usage Examples:

import { stagger, animate, style, query } from '@angular/animations';

// Basic stagger effect
const basicStagger = stagger('100ms', [
  animate('300ms', style({ opacity: 1, transform: 'translateY(0)' }))
]);

// Stagger with query for list items
const listStagger = query('.list-item', [
  style({ opacity: 0, transform: 'translateX(-50px)' }),
  stagger('150ms', [
    animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', style({
      opacity: 1,
      transform: 'translateX(0)'
    }))
  ])
]);

// Reverse stagger (negative timing)
const reverseStagger = stagger('-100ms', [
  animate('300ms', style({ opacity: 0 }))
]);

Complex Composition Examples

Parallel and Sequential Combination

import { trigger, transition, group, sequence, animate, style, query, stagger } from '@angular/animations';

trigger('complexAnimation', [
  transition(':enter', [
    // First, set initial styles
    style({ opacity: 0 }),
    
    // Then run parallel animations
    group([
      // Main element animation
      sequence([
        animate('200ms', style({ opacity: 1 })),
        animate('300ms', style({ transform: 'scale(1.05)' })),
        animate('200ms', style({ transform: 'scale(1)' }))
      ]),
      
      // Child elements animation
      query('.child', [
        style({ opacity: 0, transform: 'translateY(20px)' }),
        stagger('50ms', [
          animate('300ms ease-out', style({
            opacity: 1,
            transform: 'translateY(0)'
          }))
        ])
      ], { optional: true })
    ])
  ])
])

Card Grid Animation

trigger('cardGrid', [
  transition(':enter', [
    query('.card', [
      style({
        opacity: 0,
        transform: 'translateY(50px) scale(0.8)'
      }),
      stagger('100ms', [
        group([
          animate('300ms cubic-bezier(0.25, 0.46, 0.45, 0.94)', 
            style({ opacity: 1 })
          ),
          animate('400ms cubic-bezier(0.68, -0.55, 0.265, 1.55)', 
            style({ transform: 'translateY(0) scale(1)' })
          )
        ])
      ])
    ])
  ]),
  
  transition(':leave', [
    query('.card', [
      stagger('-50ms', [
        animate('200ms ease-in', style({
          opacity: 0,
          transform: 'translateY(-20px) scale(0.9)'
        }))
      ])
    ])
  ])
])

Modal Animation with Backdrop

trigger('modalAnimation', [
  transition(':enter', [
    group([
      // Backdrop fade in
      query('.backdrop', [
        style({ opacity: 0 }),
        animate('200ms', style({ opacity: 1 }))
      ]),
      
      // Modal slide and scale in
      query('.modal', [
        style({
          opacity: 0,
          transform: 'translateY(-50px) scale(0.9)'
        }),
        sequence([
          animate('150ms', style({ opacity: 1 })),
          animate('250ms cubic-bezier(0.34, 1.56, 0.64, 1)', 
            style({ transform: 'translateY(0) scale(1)' })
          )
        ])
      ])
    ])
  ]),
  
  transition(':leave', [
    group([
      // Modal slide and scale out
      query('.modal', [
        animate('200ms ease-in', style({
          opacity: 0,
          transform: 'translateY(-50px) scale(0.9)'
        }))
      ]),
      
      // Backdrop fade out (slower)
      query('.backdrop', [
        animate('300ms', style({ opacity: 0 }))
      ])
    ])
  ])
])

Query Selectors

CSS Selector Support

// Element selectors
query('div', animation)
query('.class-name', animation)
query('#element-id', animation)

// Attribute selectors
query('[data-animate]', animation)
query('[type="button"]', animation)

// Pseudo-selectors
query(':first-child', animation)
query(':last-child', animation)
query(':nth-child(odd)', animation)

// Combinators
query('.parent > .child', animation)
query('.parent .descendant', animation)
query('.sibling + .next', animation)

Special Angular Selectors

// All direct children
query(':enter', animation)  // Entering elements
query(':leave', animation)  // Leaving elements

// Self reference
query(':self', animation)   // The element itself

// Animation state selectors
query('@triggerName', animation)          // Elements with specific trigger
query('@*', animation)                    // Elements with any trigger
query('@triggerName.start', animation)   // Elements starting animation
query('@triggerName.done', animation)    // Elements finishing animation

Query Options

// Optional queries (won't throw if no elements found)
query('.might-not-exist', animation, { optional: true })

// Limited queries (animate only first N elements)
query('.item', animation, { limit: 5 })

// Combined options
query('.optional-item', animation, { 
  optional: true, 
  limit: 3,
  params: { duration: '500ms' }
})

Performance Considerations

Efficient Grouping

// Efficient: Group transforms together
group([
  animate('300ms', style({ transform: 'translateX(100px) scale(1.2)' })),
  animate('300ms', style({ opacity: 0.5 }))
])

// Less efficient: Separate transform animations
group([
  animate('300ms', style({ transform: 'translateX(100px)' })),
  animate('300ms', style({ transform: 'scale(1.2)' })),
  animate('300ms', style({ opacity: 0.5 }))
])

Optimized Staggering

// Good: Stagger with reasonable delays
stagger('100ms', animate('300ms', style({ opacity: 1 })))

// Consider performance: Very short staggers on many elements
stagger('10ms', animate('300ms', style({ opacity: 1 })))  // May impact performance