Different solution using state, seems like a better approach!
import { NgStyle } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { provideAnimations } from '@angular/platform-browser/animations';
import {
animate,
keyframes,
state,
style,
transition,
trigger,
} from '@angular/animations';
const stylesArr = [
style({ transform: 'scale(1)', offset: 0 }),
animate(
'{{countDown}}s',
keyframes([
style({ transform: 'scale(1)', offset: 0 }),
style({ transform: 'scale(0.5)', offset: 1 }),
])
),
];
@Component({
selector: 'app-root',
standalone: true,
imports: [NgStyle, FormsModule],
animations: [
trigger('animate', [
state('neutral', style({ transform: 'scale(1)' })),
state('executed', style({ transform: 'scale(0.5)' })),
transition(
'executed => neutral',
animate(
'{{countDown}}s',
keyframes([
style({ transform: 'scale(1)', offset: 0 }),
style({ transform: 'scale(0.5)', offset: 1 }),
])
)
),
]),
],
template: `
<div id="countdown">
<div id="countdown-number">{{ countDown }}</div>
<svg>
<circle #circle r="18" cx="20" cy="20" class="needed-styles" [@animate]="{
value:state, params:{
countDown:countDown,
}
}"></circle>
</svg>
</div>
<input [(ngModel)]="countDown" (ngModelChange)="change()"/>
`,
styles: [
`
.needed-styles {
animation-timing-function: linear;
animation-delay: 0s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: running;
animation-timeline: auto;
animation-range-start: normal;
animation-range-end: normal;
}
`,
],
})
export class App {
state = 'executed';
countDown = 3;
ngOnInit() {
setTimeout(() => {
this.state = 'neutral';
});
}
change() {
this.state = 'executed';
setTimeout(() => {
this.state = 'neutral';
});
}
}
bootstrapApplication(App, {
providers: [provideAnimations()],
});
This is my solution using angular animations, we can pass the input to angular animation the variable that holds the countdown duration inside the params property, Then I apply all the other static styles using a class and the dynamic property can be assigned using {{countDown}}s, by setting the transition for :increment and :decrement we can restart the animation!
import { NgStyle } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { provideAnimations } from '@angular/platform-browser/animations';
import {
animate,
keyframes,
style,
transition,
trigger,
} from '@angular/animations';
const stylesArr = [
style({ transform: 'scale(1)', offset: 0 }),
animate(
'{{countDown}}s',
keyframes([
style({ transform: 'scale(1)', offset: 0 }),
style({ transform: 'scale(0.5)', offset: 1 }),
])
),
];
@Component({
selector: 'app-root',
standalone: true,
imports: [NgStyle, FormsModule],
animations: [
trigger('animate', [
transition('* => *', stylesArr),
transition(':increment', stylesArr),
transition(':decrement', stylesArr),
]),
],
template: `
<div id="countdown">
<div id="countdown-number">{{ countDown }}</div>
<svg>
<circle #circle r="18" cx="20" cy="20" class="needed-styles" [@animate]="{
value:countDown, params:{
countDown:countDown,
}
}"></circle>
</svg>
</div>
<input [(ngModel)]="countDown"/>
`,
styles: [
`
.needed-styles {
animation-timing-function: linear;
animation-delay: 0s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: running;
animation-timeline: auto;
animation-range-start: normal;
animation-range-end: normal;
}
`,
],
})
export class App {
animationToggle = false;
countDown = 3;
}
bootstrapApplication(App, {
providers: [provideAnimations()],
});