This unique animation controls the rotation of the mat expansion indicator. The animation, found here, simply uses a rotate(180deg)
directive to rotate the indicator 180 degrees clockwise. Initially, the indicator is set to rotate(45deg)
, displaying a downward icon when the panel is closed and an upward icon when open with a clockwise animation.
If you try to adjust the rotation using:
.mat-expansion-indicator::after {
transform: rotate(225deg) !important;
}
The result is that the icon rotates upwards when the panel is closed. When clicked, it then rotates by 180 degrees clockwise. This configuration prevents showing a downward icon when closed and an upward one when opened due to the rotate(225deg)
setting. Changing this sacrifices inward turning as the animation initiates clockwise instead of counterclockwise, which is what we need.
Unfortunately, Angular Material does not provide a built-in way to override default animations, as documented here.
To achieve the desired counterclockwise rotation, my solution involves completely disabling default animations on mat-expansion-panel-header
and creating a custom directive to mimic the desired animation in a counterclockwise direction.
Firstly, disable the default animation on mat-expansion-panel-header
:
<mat-expansion-panel-header [@.disabled]="true">
Create new animations in styles.css
that match the timing and behavior of the default animation:
@keyframes inwards-open {
0% { transform: rotate(0deg); }
100% { transform: rotate(-135deg); }
}
@keyframes inwards-close {
0% { transform: rotate(-135deg); }
100% { transform: rotate(0deg); }
}
.openAnimate {
animation: inwards-open 225ms cubic-bezier(0.4,0.0,0.2,1) !important;
}
.closeAnimate {
animation: inwards-close 225ms cubic-bezier(0.4,0.0,0.2,1) !important;
}
Next, implement a custom directive that manages the animation status based on panel opening and closing events:
import { Directive, ElementRef, HostListener, AfterViewInit, OnDestroy } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
@Directive({
selector: '[appCustomMatExpansionToggle]'
})
export class CustomMatExpansionToggleDirective implements AfterViewInit, OnDestroy {
private elem: HTMLSpanElement;
private uns: Subscription;
constructor(private elRef: ElementRef) {}
ngAfterViewInit() {
this.elem = this.elRef.nativeElement.querySelector(".mat-expansion-indicator");
this.uns = fromEvent(this.elem, 'animationend').subscribe(() => {
this.elem.classList.remove("openAnimate");
this.elem.classList.remove("closeAnimate");
});
}
@HostListener("opened")
onOpen() {
this.elem.classList.add("openAnimate");
}
@HostListener("closed")
onClose() {
this.elem.classList.add("closeAnimate");
}
ngOnDestroy() {
this.uns.unsubscribe();
}
}
Finally, apply the custom directive to mat-expansion-panel
:
<mat-expansion-panel appCustomMatExpansionToggle>
For a demonstration, check out this working demo: https://stackblitz.com/edit/indicatorrotation-mp73uh