I need a way to configure my animations dynamically. For example, I want my consumer to be able to pass as Input of the numbers of the animate declaration or any CSS property.
animations: [
trigger('heroState', [
state('inactive', style({
backgroundColor: '#eee',
transform: this.transform
})),
state('active', style({
backgroundColor: '#cfd8dc',
transform: this.transform
})),
transition('inactive => active', animate(this.animateInActive),
transition('active => inactive', animate('100ms ease-out'))
])
]
class Cmp {
#Input() transform = 'scale(1)';
#Input() animateInActive = '200ms ease-out';
}
Related
I'm trying to create a component that will slide in content projected with ng-content (This is the Stackblitz).
This is the animation.
import {
trigger,
state,
style,
transition,
animate,
group,
} from '#angular/animations';
export const SlideInOutAnimation = [
trigger('slideInOut', [
state(
'in',
style({
height: '100%',
opacity: '1',
visibility: 'visible',
})
),
state(
'out',
style({
height: '0px',
opacity: '0',
visibility: 'hidden',
})
),
transition('in => out', [
group([
animate(
'400ms ease-in-out',
style({
opacity: '0',
})
),
animate(
'600ms ease-in-out',
style({
'max-height': '0px',
})
),
animate(
'700ms ease-in-out',
style({
visibility: 'hidden',
})
),
]),
]),
transition('out => in', [
group([
animate(
'1ms ease-in-out',
style({
visibility: 'visible',
})
),
animate(
'600ms ease-in-out',
style({
height: '100%',
})
),
animate(
'800ms ease-in-out',
style({
opacity: '1',
})
),
]),
]),
]),
];
And this is the HelloComponent refactored to use the animation.
import {
Component,
Input,
OnChanges,
SimpleChanges,
ViewEncapsulation,
} from '#angular/core';
import { SlideInOutAnimation } from './animation';
#Component({
animations: [SlideInOutAnimation],
selector: 'hello',
template: `<ng-content [#slideInOut]="animationState"></ng-content>`,
styles: [
`hello { position: absolute;
background: red;
height: 0px;
bottom: 0;
visibility: hidden;
}`,
],
encapsulation: ViewEncapsulation.None,
})
export class HelloComponent implements OnChanges {
#Input() animationState: string = 'in';
ngOnChanges(changes: SimpleChanges) {
console.log(changes.animationState);
}
}
When the animationState is toggled it is logged in the console. However, the projected content does not animate in.
Any ideas?
As suggested by Andrei in the comments I use #HostBinding instead and now it works.
#HostBinding('#slideInOut')
get currentAnimationState() {
return this.animationState;
}
This is a new Stackblitz with the working demo.
I have a button on top of my slider, so currently I am able to slide the animation from left to right and vice versa on the Button click.
But when I do that button is moving separately and feel like it is detached from slider. How to make it stick to slider any help would be great.
In this EXAMPLE, we can see how there is Feedback area which on hover slides the panel, so in my case that Feedback is a button should act the same way.
HTML:
<div class="container" [#slide]="slideOpen ? true : false">
<button [class.button-resize-expand]="!slideOpen"
[class.button-resize-collapse]="slideOpen"
(click)="togglePanel()">
<i class="rotate-chevron" [class.rotate-clicked]="!slideOpen">{{slideOpen ? 'chevron_left' : 'chevron_right'}}</i>
</button>
<div *ngIf="slideOpen"></div>
</div>
TS:
animations: [
trigger('slide', [
transition('* <=> *', [
group([
query(':enter', [
style({transform: 'translateX(-100%)'}),
animate('.3s', style({transform: 'translateX(0%)'}))
], {optional: true}),
query(':leave', [
style({transform: 'translateX(0%)'}),
animate('.3s', style({transform: 'translateX(-100%)'}))
], {optional: true}),
])
])
])
]
Here is the stackblitz-code and it works, I used little CSS and another trigger element in angular animations.
Here are resources which can be used for reference
HTML:
<div class="container" [#slide]="isOpen ? true : false">
<button [class.button-resize-expand]="!isOpen"
[class.button-resize-collapse]="isOpen"
(click)="togglePanel()" [#openClose] ="isOpen ? 'open': 'closed'">
<mat-icon class="rotate-chevron" [class.rotate-clicked]="!isOpen">{{panelExpanded ? 'chevron_left' : 'chevron_right'}}</mat-icon>
</button>
<div *ngIf="isOpen" class="expandable-panel">
<div class="current-task-info">
So much data here damn!
</div>
</div>
</div>
TS:
type isOpen = true|false;
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.scss' ],
animations: [
trigger('slide', [
transition('* <=> *', [
group([
query(':enter', [
style({transform: 'translateX(-100%)'}),
animate('.3s', style({transform: 'translateX(0%)'}))
], {optional: true}),
query(':leave', [
style({transform: 'translateX(0%)'}),
animate('.3s', style({transform: 'translateX(-100%)'}))
], {optional: true}),
])
])
]),
trigger('openClose', [
// ...
state('open', style({
animation: 'slidefront .3s linear 0s forwards'
})),
state('closed', style({
animation: 'slideback .3s linear 0s backwards, bounce 1.5s'
}))
]),
]
})
export class AppComponent {
isOpen = true;
togglePanel(): void {
this.isOpen = !this.isOpen;
}
}
CSS:
#keyframes slidefront {
from {left: 0%;}
to {left: 80%;}
}
#keyframes slideback {
from {right: 5%;}
to {right: 80%;}
}
In this stackblitz demo when we click Create success the components view display is animated (From opacity 0 to opacity 1 over 5 seconds.).
If we clear the container (this.container.clear()) the removal of the element is not animated. The animations attribute looks like this:
animations: [
trigger('fadeInOut', [
transition(':enter', [
style({ opacity: 0 }),
animate(5000, style({ opacity: 1 }))
]),
transition(':leave', [
animate(5000, style({ opacity: 0 }))
])
])
],
How would we enable the triggering of the leave animation in this case?
Update your alert.component.ts to this:
import { Component, Input, EventEmitter, Output } from '#angular/core';
import { trigger, style, animate, transition } from '#angular/animations';
#Component({
selector: "alert",
template: `
<section [#fadeInOut]>
<h1 (click)="output.next('output')">Alert {{type}}</h1>
<section>
`,
styles: [`
:host {
display: block;
overflow: hidden;
}`],
animations: [
trigger('fadeInOut', [
transition(':enter', [
style({ opacity: 0 }),
animate(5000, style({ opacity: 1 }))
]),
transition(':leave', [
animate(5000, style({ opacity: 0 }))
])
])
],
host: { '[#fadeInOut]': 'in' }
})
export class AlertComponent {
#Input() type: string = "success";
#Output() output = new EventEmitter();
}
Thanks to https://stackblitz.com/edit/angular-animate-dynamically-created-component?file=app%2Fhello.component.ts
How do you handle resizing the window with Angular Animations properties like width?
For example:
This code will animate the width of an element and for all sizes will not flood off screen or look too small.
Desktop:
mobile:
Until the user resizes the screen. On resize the animation is completed and therefore will not react to the window resize.
function setEndWidth() {
if(MatchMediaService.bp('sm')){
return '70vw';
} else {
return '30vw';
}
}
function setStartWidth() {
if(MatchMediaService.bp('sm')){
return '0px';
} else {
return '20vw';
}
}
#Component({
moduleId: module.id,
selector: 'menu-container',
templateUrl: 'menu.html',
styleUrls: ['./menu.scss'],
animations: [
trigger('goodmorning', [
state('void', style({
'width': '0',
'transform': 'rotate(0)',
'transform-origin': '0 0'
})),
state('harwood', style({
'width': setEndWidth(),
'transform': 'rotate(-30deg)',
'transform-origin': '0 0'
})),
transition('void => *', animate(2000, keyframes([
style({
'width': '0',
'transform': 'rotate(0)',
'transform-origin': '0 0'
}),
style({
'width': setStartWidth(),
'transform': 'rotate(0)',
'transform-origin': '0 0'
}),
style({
'width': setEndWidth(),
'transform': 'rotate(-30deg)',
'transform-origin': '0 0'
})
]))),
transition('void => *', animate('2.5s 0s cubic-bezier(0.455, 0.03, 0.515, 0.955)'))
])
]
})
HTML:
<line-thing [#goodmorning]="'harwood'"></line-thing>
In all the angular 2 API animation examples, the animation handler is implemented on innerHtml,
I would like to put it directly on the component selector.
In the example below, [#visibility]="visibility" is on the div tag, but I want it on the selector: 'vps-node' so my template is simply <ng-content> with out the parent div tag
#Component({
selector: 'vps-node',
template: `<div [#visibility]="visibility"><ng-content></ng-content></div>`,
animations: [
trigger('visibility', [
state('in', style({ transform: 'translateX(0)' })),
transition('void => *', [
animate(300, keyframes([
style({ opacity: 0, transform: 'translateX(-100%)', offset: 0 }),
style({ opacity: 1, transform: 'translateX(15px)', offset: 0.3 }),
style({ opacity: 1, transform: 'translateX(0)', offset: 1.0 })
]))
]),
transition('* => void', [
animate(300, keyframes([
style({ opacity: 1, transform: 'translateX(0)', offset: 0 }),
style({ opacity: 1, transform: 'translateX(-15px)', offset: 0.7 }),
style({ opacity: 0, transform: 'translateX(100%)', offset: 1.0 })
]))
])
])
]
})
Thought i'd share this but I have managed to make the animation trigger on the host element.
Check the following code where I handle page transitions.
#Component({
selector: 'styles',
templateUrl: './styles.template.html',
host: {
'(#routeAnimation.start)': 'pageEnterStarted($event)',
'(#routeAnimation.done)': 'pageEnterCompleted($event)',
'[#routeAnimation]': 'true',
},
animations: [
trigger('routeAnimation', [
state('*', style({ opacity: 1})),
transition('void => *', [
style({ opacity: 0}),
animate(250)
]),
transition('* => void', animate(250, style({opacity: 0})))
])
]
})
export class StylesComponent {
pageEnterStarted() {
}
pageEnterCompleted() {
}
}
Turns out the host:property needed to be in a bracket.
host: { '[ #visibility ]': 'visibility' },
selector: 'vps-row-node',
host: { '[#visibility]': 'visibility' },
template: `<ng-content></ng-content>`,
animations: [...]