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

duffy picture duffy · Nov 28, 2017 · Viewed 7.9k times · Source

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}))
      ])
    ])
  ]
})

Answer

br.julien picture br.julien · Nov 28, 2017

Here is how I did using Angular animations states:

animations.ts

import { trigger, style, animate, transition, state } from '@angular/animations';

export const fade = [
  trigger('fade', [
    state('in', style({ 'opacity': '1' })),
    state('out', style({ 'opacity': '0' })),
    transition('* <=> *', [
      animate(1000)
    ])
  ])
];

app.component.html

<img [@fade]="state" (@fade.done)="onDone($event)" [src]="imageSource" width="500" (click)="onClick()" />

I used (@fade.done) which is a feature of Angular animations that allows you to call a method once your animation is done.

Here, once the first animation has faded to have an opacity of 0, I changed the image path, and changed the animation state, to fade again and have the opacity value back to 1.

app.component.ts

export class AppComponent implements OnInit {
  choice = 2;
  state = 'in';
  counter = 0;
  enableAnimation = false;
  imageSource = '';
  imgSrc1 = 'firstPath';
  imgSrc2 = 'secondPath';

  ngOnInit() {
    this.imageSource = this.imgSrc1;
  }

  onClick() {
    this.enableAnimation = true;
    this.counter = 0;
    this.toggleState();
  }

  toggleImg() {
    if (this.choice === 1) {
      this.imageSource = this.imgSrc1;
      this.choice = 2;
    } else {
      this.imageSource = this.imgSrc2;
      this.choice = 1;
    }
  }

  onDone($event) {
    if (this.enableAnimation) {
      if (this.counter === 1) {
        this.toggleImg();
      }
      this.toggleState();
    }
  }

  toggleState() {
    if (this.counter < 2) {
      this.state = this.state === 'in' ? 'out' : 'in';
      this.counter++;
    }
  }
}

This is obviously a lot of code for a small animation though.

Here is a StackBlitz example I made for this : https://stackblitz.com/edit/angular-anim-fade-img