htmlcssangularanimationangular-animations

Angular Animation Using stagger from a query with custom delay


I'm using an Angular 15 animation to handle the showing of images on a page.

transition(':enter', [
  query('.chat', [
    style({ opacity: 0, transform: 'translateY(30px)' }),
    stagger('0.75s', [ 
      animate('250ms', style({ opacity: 1 , transform: 'translateY(0)' }))
    ])
  ])
])

This works fine, I have a div with child images assigned a class "chat".

      <div *ngIf="isVisible" @fadeSlideInOutStagger >      
        <img class="chat" src="/chat1.webp"/> 
        <img class="chat" src="/chat2.webp"/> 
        <img class="chat" src="/chat3.webp"/> 
      </div>

But I want to introduce a delay between the child elements. I want the second image to have twice as long of a delay as the first and the last half as long. at the moment there is a fixed delay as defined in the stagger param.

Can this be done. I don't seem to have access to the indivdual child element as the effect is handled by the parent div.

If the effect can't handle it natively, what about maybe handling the introduction of the child elements programatically. Not sure how feasible that would be.

Does anyone have experience getting this to work?


Solution

  • You can define the animation at the child level, then pass in a params property called delay, this can be used to introduce the delay in animation.

    <div *ngIf="isVisible">      
      <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:1000 }}"/> 
      <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:2000 }}"/> 
      <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:3000 }}"/> 
    </div>
    

    Animation:

    animations: [
      trigger('childAnimation', [
        transition(
          ':enter',
          [
            style({ opacity: 0, transform: 'translateY(30px)' }),
            animate(
              '2000ms {{delay}}ms',
              style({ opacity: 1, transform: 'translateX(0)' })
            ),
          ],
          { params: { delay: 1 } }
        ),
      ]),
    ],
    

    Full Code:

    import { Component, inject, Injectable, Input, OnInit } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { provideAnimations } from '@angular/platform-browser/animations';
    import { Observable, of, Subject, BehaviorSubject } from 'rxjs';
    import { CommonModule } from '@angular/common';
    import {
      transition,
      query,
      style,
      stagger,
      animate,
      trigger,
    } from '@angular/animations';
    @Component({
      selector: 'app-root',
      imports: [CommonModule],
      template: `
        <div *ngIf="isVisible">      
          <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:1000 }}"/> 
          <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:2000 }}"/> 
          <img class="chat" src="https://placehold.co/200x200/jpeg" [@childAnimation]="{value: '', params:{ delay:3000 }}"/> 
        </div>
      `,
      animations: [
        trigger('childAnimation', [
          transition(
            ':enter',
            [
              style({ opacity: 0, transform: 'translateY(30px)' }),
              animate(
                '2000ms {{delay}}ms',
                style({ opacity: 1, transform: 'translateX(0)' })
              ),
            ],
            { params: { delay: 1 } }
          ),
        ]),
      ],
    })
    export class App {
      isVisible = true;
    }
    
    bootstrapApplication(App, {
      providers: [provideAnimations()],
    });
    

    Stackblitz Demo