I've created a bootstrap modal component for angular, but I'd like to choose from which direction the modal enters the window. I've
At the moment my EnterLeaveAnimation
looks like this:
export const EnterLeaveAnimation =
trigger('enterLeave', [
transition(
':enter', [
style({ top: '-50%' }), // <-- I should be able to change this dynamically, eg: left: '-50%'
animate('{{ duration }} ease-in-out', style({ top: 0 })),
], {
params: {
duration: '500ms'
}
}
),
transition(
':leave', [
animate('{{ duration }} ease-in-out', style({ top: '-50%' }))
], {
params: {
duration: '500ms'
}
}
),
]);
ATM this animation is applied to the div like this:
<div [@fadeInOut] [@enterLeave] *ngIf="isOpen">
<!-- [fromDirection]="fromDirection" -->
...
</div>
<div [@fadeInOut] *ngIf="isOpen">
<div [class.show]="isOpen"></div>
</div>
But would eventually end up looking like this:
<div [@fadeInOut] [@enterLeave]="{ value: isOpen ? ':enter' : ':leave', params: { fromDirection: fromDirection } }" *ngIf="isOpen">
...
</div>
<div [@fadeInOut] *ngIf="isOpen">
<div [class.show]="isOpen"></div>
</div>
The problem I have now, is that I can't figure out how I can modify my EnterLeaveAnimation
to switch
on the fromDirection
and apply different style rules accordingly:
style({ top: '-50%' })
OR
style({ bottom: '-50%' })
OR
style({ left: '-50%' })
OR
style({ right: '-50%' })
As you can see, I'm already passing my duration
parameter on to the animation, but how do I switch
on a parameter and use the style
function accordingly?
CodePudding user response:
I'am afraid that you should make the animation "manually"
Don't worry, it's easy, see this SO for a simple e.g.
With this idea we can defined in your host component some like
animate(element: any, state: string, background: boolean) {
if (element) {
let customstyle =
state == 'enter'
? background
? { opacity: 0 }
: { top: '-50%', left: '0', opacity: 0 }
: background
? { opacity: 1 }
: { top: '-100%', left: '0', opacity: 1 };
if (!background) {
switch (this.fromDirection) {
case 'bottom':
customstyle =
state == 'enter'
? { top: '50%', left: '0', opacity: 0 }
: { top: '100%', left: '0', opacity: 0 };
break;
case 'left':
customstyle =
state == 'enter'
? { top: '0', left: '-100%', opacity: 0 }
: { top: '0', left: '-100%', opacity: 0 };
break;
case 'right':
customstyle =
state == 'enter'
? { top: '0', left: '100%', opacity: 0 }
: { top: '0', left: '100%', opacity: 0 };
break;
}
}
const myAnimation =
state == 'enter'
? this.builder.build([
style(customstyle),
animate(this.timing, style({ top: 0, left: 0, opacity: 1 })),
])
: this.builder.build([animate(this.timing, style(customstyle))]);
this.player = myAnimation.create(element);
if (state == 'leave') {
this.player.onDone(() => {
this.componentInstance.instance.isOpen = false;
});
}
this.player.play();
}
}
You also define in your host component a function close
close() {
this.isOpen = false;
this.animate(
(this.componentInstance.instance as any).modal.nativeElement,
'leave',
false
);
this.animate(
(this.componentInstance.instance as any).modalBackground.nativeElement,
'leave',
true
);
}
And use the getter to call to animate
@Input() set isOpen(value: boolean) {
this._isOpen = value;
if (this.componentInstance) {
if (value) {
this.componentInstance.instance.isOpen = value;
this.animate(
(this.componentInstance.instance as any).modal.nativeElement,
'enter',
false
);
this.animate(
(this.componentInstance.instance as any).modalBackground
.nativeElement,
'enter',
true
);
} else {
this.animate(
(this.componentInstance.instance as any).template.nativeElement,
'leave',
false
);
this.animate(
(this.componentInstance.instance as any).modalBackground
.nativeElement,
'leave',
true
);
}
}
this.isOpenChange.emit(value);
}
As you see the animate "reemplace" your animations, but we can not use *ngIf
to show/hide the modal else [ngClass]
in the way
<!---modal.component.html-->
<div #modal [ngClass]="isOpen?'modal d-block':'modal d-none'" class='modal d-block'>
<div >
<div >
<ng-container *ngTemplateOutlet="template"></ng-container>
</div>
</div>
</div>
<div #modalBackground [ngClass]="isOpen?'d-block':'d-none'">
<div [class.show]="isOpen"></div>
</div>
See how you use template reference variable to get the background and the content, so we need use ViewChild
@ViewChild('modal') modal:ElementRef
@ViewChild('modalBackground') modalBackground:ElementRef
NOTE: In the stackblitz I "hardcode the "timing", you can use an @Input or another way.