Creating an Angular Material Expansion Indicator Animation | Task

Ole Ersoy
Jun - 24  -  2 min

Scenario

We are creating a custom expansion panel and we want to animate a expand_circle_down material icon such that it rotates 180 degrees over 300 milliseconds when the panel is expanded or collapsed.

Approach

Create a new Stackblitz and add @angular/material as a dependency.

Also within index.html add the material icons CDN link.

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>

Next replace the app.component.ts implementation with this.

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

@Component({
  selector: 'my-app',
  template: `<mat-icon
  [@animationTrigger]="state"
  aria-hidden="false"
  aria-label="Chevron Animation"
  >expand_circle_down</mat-icon>
`,
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('animationTrigger', [
      state(
        'expanded',
        style({
          transform: 'rotate(180deg)',
        })
      ),
      transition('collapsed => expanded', animate('300ms ease-in')),
      transition('expanded => collapsed', animate('300ms ease-out')),
    ]),
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
  public isExpanded = false;
  @HostListener('click')
  @HostListener('keydown.enter')
  @HostListener('keydown.space')
  public toggle() {
    this.isExpanded = !this.isExpanded;
  }

  get state(): string {
    return this.isExpanded ? 'expanded' : 'collapsed';
  }
}

Within the template we have added the icon we wish to animate.

The @animationTrigger mat-icon property assignment points to our animation state.

<mat-icon  [@animationTrigger]="state"  aria-hidden="false"  aria-label="Chevron Animation"  >expand_circle_down</mat-icon>

The animation state getter returns expanded or collapsed depending on the value of the isExpanded property.

get state(): string {
  return this.isExpanded ? 'expanded' : 'collapsed';
}

The Angular Animation API is used to setup the animations definition within the @Component declaration.

animations: [
   trigger('animationTrigger', 
   [state('expanded',
             style({
            transform: 'rotate(180deg)',
        })),       
   transition('collapsed => expanded', 
               animate('300ms ease-in')),      
   transition('expanded => collapsed', 
   animate('300ms ease-out')),    ]),

Note that the animation trigger is what we added to the mat-icon @animation property in order to trigger the animation.

[@animationTrigger]="state"

The @HostListener('click') decoration on the toggle() method causes the toggle() to fire whenever the user clicks on the icon, and this updates the state triggering the animation.

Demo