feat(deposit): implement modal animations with GSAP

This commit is contained in:
Jan-Marlon Leibl 2025-03-06 11:52:31 +01:00
parent c651337d30
commit 08a1a5e877
Signed by: jleibl
GPG key ID: 300B2F906DC6F1D5
9 changed files with 177 additions and 17 deletions

View file

@ -1,6 +1,6 @@
@if (successful) {
<div class="modal-bg">
<div class="modal-card">
<div #modalBg class="modal-bg">
<div #modalCard class="modal-card">
<h2 class="modal-heading text-center">Bestätigung</h2>
<p class="py-2">Der Vorgang wurde erfolgreich abgeschlossen.</p>
<button type="button" class="button-primary w-full py-2 my-auto" (click)="closeModal()">

View file

@ -1,4 +1,6 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { ModalAnimationService } from '../../services/modal-animation.service';
import gsap from 'gsap';
@Component({
selector: 'app-confirmation',
@ -6,11 +8,36 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
imports: [],
templateUrl: './confirmation.component.html',
})
export class ConfirmationComponent {
export class ConfirmationComponent implements AfterViewInit, OnDestroy {
@Input() successful = true;
@Output() close = new EventEmitter<void>();
@ViewChild('modalBg') modalBg!: ElementRef;
@ViewChild('modalCard') modalCard!: ElementRef;
constructor(private modalAnimationService: ModalAnimationService) {}
ngAfterViewInit() {
if (this.successful) {
this.openModal();
}
}
ngOnDestroy() {
gsap.killTweensOf([this.modalBg?.nativeElement, this.modalCard?.nativeElement]);
}
private openModal() {
this.modalAnimationService.openModal(
this.modalCard.nativeElement,
this.modalBg.nativeElement
);
}
public closeModal() {
this.close.emit();
this.modalAnimationService.closeModal(
this.modalCard.nativeElement,
this.modalBg.nativeElement,
() => this.close.emit()
);
}
}

View file

@ -11,15 +11,15 @@
</div>
<div class="hidden md:flex items-center space-x-4">
<div
class="text-white font-bold bg-deep-blue-contrast rounded-full px-4 py-2 text-sm hover:bg-deep-blue-contrast/80 hover:cursor-pointer hover:scale-105 transition-all active:scale-95 select-none duration-300"
>
<span>Balance: {{ balance() | currency: 'EUR' : 'symbol' : '1.2-2' }}</span>
</div>
@if (!isLoggedIn) {
<button (click)="login()" class="button-primary px-4 py-1.5">Anmelden</button>
}
@if (isLoggedIn) {
<div
class="text-white font-bold bg-deep-blue-contrast rounded-full px-4 py-2 text-sm hover:bg-deep-blue-contrast/80 hover:cursor-pointer hover:scale-105 transition-all active:scale-95 select-none duration-300"
>
<span>Balance: {{ balance() | currency: 'EUR' : 'symbol' : '1.2-2' }}</span>
</div>
<button (click)="logout()" class="button-primary px-4 py-1.5">Abmelden</button>
}
</div>

View file

@ -0,0 +1,53 @@
import { Injectable } from '@angular/core';
import gsap from 'gsap';
@Injectable({
providedIn: 'root'
})
export class ModalAnimationService {
private readonly defaultDuration = 0.3;
private readonly defaultEase = 'power2.out';
openModal(modalElement: HTMLElement, overlayElement: HTMLElement) {
gsap.set(overlayElement, { opacity: 0, display: 'block' });
gsap.set(modalElement, {
opacity: 0,
scale: 0.95,
y: 20,
display: 'block'
});
gsap.to(overlayElement, {
opacity: 1,
duration: this.defaultDuration,
ease: this.defaultEase
});
gsap.to(modalElement, {
opacity: 1,
scale: 1,
y: 0,
duration: this.defaultDuration,
ease: this.defaultEase
});
}
closeModal(modalElement: HTMLElement, overlayElement: HTMLElement, onComplete?: () => void) {
gsap.to([overlayElement, modalElement], {
opacity: 0,
duration: this.defaultDuration,
ease: this.defaultEase,
onComplete: () => {
gsap.set([overlayElement, modalElement], { display: 'none' });
onComplete?.();
}
});
gsap.to(modalElement, {
scale: 0.95,
y: 20,
duration: this.defaultDuration,
ease: this.defaultEase
});
}
}