Animate an image swap in Angular 4 (Was ng-animate-swap in AngularJS)

Viewed 10108

I'd like to animate an image swap in an angular 4 app. As the controller replaces the img src property the old image should fade away and the new appear.

In AngularJS this was possible by changing the opacity with ng-animate-swap. However, Angular 4 doesn't seem to support animate-swap. How can this be achieved without a swap trigger?

I've tried adding transitions from void to * and back for the src property but this doesn't work as expected. The first image is animated in, later the exchanges happen without animation.

@Component({
  selector: 'app-play-card',
  template: '<div (click)="loadImg()"><img [@fade]="imgsrc" src="{{ imgsrc }}" /></div>',
  styleUrls: ['./play-card.component.css'],
  animations: [
    trigger('fade', [
      transition('void => *', [
        style({opacity: 0.1}),
        animate(5000, style({opacity: 1}))
      ]),
      transition('* => void', [
        style({opacity: 0.9}),
        animate(5000, style({opacity: 0}))
      ])
    ])
  ]
})
3 Answers

I did filter out as much as possible not to overload the code. I hope the code is simple enough for you to integrate it.

gallery.component.ts:

import { Component, Input } from '@angular/core';
import { trigger, style, animate, transition } from '@angular/animations';

const animations = trigger('imgAnimation', [
    transition(':enter', [
        style({ opacity: 0 }),
        animate('500ms', style({ opacity: 1 })),
    ]),
    transition(':leave', [
        animate('300ms', style({ opacity: 0 }))
    ])
]);

@Component({
    selector: 'app-gallery',
    templateUrl: './gallery.component.html',
    styleUrls: [],
    animations: [animations]
})
export class GalleryComponent {
    @Input() images: string[];

    index: number = 0;
    imageIndex: number = 0;

    onIncIndex(increment: number = 1) {
        this.index = (this.index + this.images.length + increment) % this.images.length;
        this.imageIndex = null;
    }

    onDone() {
        this.imageIndex = this.index;
    }
}

gallery.component.html:

<picture>
    <ng-container *ngFor="let img of images; index as i">
        <img *ngIf="i === imageIndex" 
             @imgAnimation
             (@imgAnimation.done)="onDone()"
             [src]="img">
    </ng-container>
</picture>

<div>
    <button (click)="onIncIndex(-1)">Previous</button>
    <button (click)="onIncIndex()">Next</button>
</div>

If you are working with 2 images.... You could do something like this..

<mat-icon>
  <img
    [@fadeIn]
    *ngIf="myBool"
    src="assets/svg-icons/back.svg"
    alt=""
  />
  <img
    [@fadeIn]
    *ngIf="!myBool"
    src="assets/svg-icons/close.svg"
    alt=""
  />
</mat-icon>

And you will want to animate only the enter state otherwise for an instance both images will be shown side by side

trigger('fadeIn', [
  transition(':enter', [style({ opacity: 0 }), animate(160)]),
  // transition(':leave', [animate(160, style({ opacity: 0 }))]),
]),

You could work around and make the images absolute and then you can animate both the enter and leave state (Because this time the image will overlap each other)

Related