feat: implement animated buttons and winner ticker component

This commit is contained in:
Jan-Marlon Leibl 2025-02-05 14:40:15 +01:00
parent 313080ceac
commit 5c34014841
No known key found for this signature in database
GPG key ID: E7B6F77BF5EDB6F7
5 changed files with 202 additions and 146 deletions

View file

@ -0,0 +1,48 @@
import { Component, Input, Output, EventEmitter, ElementRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AnimationService } from '../../../services/animation.service';
@Component({
selector: 'app-animated-button',
standalone: true,
imports: [CommonModule],
template: `
<button
(click)="handleClick($event)"
[class]="buttonClass"
[ngClass]="size === 'large' ? 'px-12 py-5 text-2xl' : 'px-8 py-3 text-sm'"
>
<div
class="absolute inset-0 rounded-full bg-white/20 opacity-0 group-hover:opacity-100 transition-all duration-500 ease-in-out"
></div>
<span class="relative z-10 group-hover:tracking-wider transition-all duration-300">
<ng-content></ng-content>
</span>
</button>
`,
styles: [],
})
export class AnimatedButtonComponent {
@Input() variant: 'primary' | 'secondary' = 'primary';
@Input() size: 'normal' | 'large' = 'normal';
@Output() buttonClick = new EventEmitter<MouseEvent>();
constructor(private animationService: AnimationService) {}
get buttonClass(): string {
const baseClass =
'relative group font-bold rounded-full transition-all duration-300 ease-out transform-gpu hover:scale-105 will-change-transform';
const variantClass =
this.variant === 'primary'
? 'bg-gradient-to-r from-emerald-500 to-emerald-400 text-black hover:shadow-xl hover:shadow-emerald-500/20'
: 'bg-white/10 text-white hover:bg-white/20';
return `${baseClass} ${variantClass}`;
}
handleClick(event: MouseEvent): void {
const elementRef = new ElementRef(event.currentTarget);
this.animationService.animateButtonClick(elementRef);
this.buttonClick.emit(event);
}
}

View file

@ -0,0 +1,98 @@
import { Component, Input, Output, EventEmitter, ElementRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Game } from '../../../services/game.service';
import { AnimatedButtonComponent } from '../animated-button/animated-button.component';
import { AnimationService } from '../../../services/animation.service';
@Component({
selector: 'app-game-card',
standalone: true,
imports: [CommonModule, AnimatedButtonComponent],
template: `
<div
class="game-card group relative overflow-hidden rounded-2xl cursor-pointer bg-black/40 backdrop-blur-xl border border-white/5
transition-all duration-500 transform hover:scale-[1.02] hover:shadow-2xl hover:shadow-emerald-500/10"
(mouseenter)="onHover($event)"
>
<div
class="absolute inset-0 bg-gradient-to-br from-emerald-500/10 to-emerald-400/10 opacity-0
group-hover:opacity-100 transition-opacity duration-500"
></div>
<img
[src]="game.imageUrl"
[alt]="game.name"
class="w-full h-64 object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div class="absolute inset-0 bg-gradient-to-t from-black via-black/80 to-transparent">
<div class="absolute bottom-0 left-0 right-0 p-8">
<div class="flex justify-between items-start mb-4">
<h3 class="text-2xl font-bold text-white tracking-tight">{{ game.name }}</h3>
<div class="flex flex-col items-end gap-2">
<span
class="bg-emerald-500/20 text-emerald-400 text-sm px-3 py-1 rounded-full font-medium animate-pulseGlow"
>
HOT 🔥
</span>
<span class="text-yellow-400 text-sm">
{{ game.lastWinner }} won {{ game.lastWin | number }}
</span>
</div>
</div>
<p class="text-white/60 mb-6 font-medium">{{ game.description }}</p>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span
class="text-emerald-400 font-bold animate-pulseGlow drop-shadow-[0_0_8px_rgba(34,197,94,0.2)]"
>
{{ game.winChance }}% Win Rate
</span>
<span class="text-yellow-400 font-medium">Max Win: {{ game.maxWin | number }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-white/60">
Min: {{ game.minBet }} | Max: {{ game.maxBet }}
</span>
<div class="flex items-center gap-2">
<span class="text-xs text-white/60">Popularity:</span>
<div class="w-24 h-1.5 bg-white/5 rounded-full overflow-hidden">
<div
class="h-full bg-gradient-to-r from-emerald-500 to-emerald-400 transition-all duration-500"
[style.width]="game.popularity + '%'"
></div>
</div>
</div>
</div>
<div class="flex justify-between items-center pt-2">
<div class="flex gap-2">
<span
*ngFor="let feature of game.features"
class="text-xs px-3 py-1 rounded-full bg-white/5 text-white/60 font-medium"
>
{{ feature }}
</span>
</div>
<app-animated-button (buttonClick)="onPlay()"> PLAY NOW </app-animated-button>
</div>
</div>
</div>
</div>
</div>
`,
styles: [],
})
export class GameCardComponent {
@Input() game!: Game;
@Output() play = new EventEmitter<void>();
constructor(private animationService: AnimationService) {}
onHover(event: MouseEvent): void {
const element = event.currentTarget as HTMLElement;
const elementRef = new ElementRef(element);
this.animationService.animateFloat(elementRef);
}
onPlay(): void {
this.play.emit();
}
}

View file

@ -0,0 +1,32 @@
import { Component, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { default as autoAnimate } from '@formkit/auto-animate';
import { Winner } from '../../../services/winner.service';
@Component({
selector: 'app-winner-ticker',
standalone: true,
imports: [CommonModule],
template: `
<div class="flex items-center gap-12 animate-marquee will-change-transform" #tickerContainer>
<span *ngFor="let winner of winners" class="flex items-center gap-3 whitespace-nowrap">
<span class="text-yellow-300 text-lg animate-pulseGlow will-change-transform">🎰</span>
<span class="font-medium">
{{ winner.name }}
<span class="text-yellow-300 font-semibold">{{ winner.isVIP ? '(VIP)' : '' }}</span>
won <span class="text-yellow-300 font-semibold">{{ winner.amount | number }}</span>
<span class="text-yellow-300">({{ winner.multiplier }}x)</span>
</span>
</span>
</div>
`,
styles: [],
})
export class WinnerTickerComponent implements AfterViewInit {
@Input() winners: Winner[] = [];
@ViewChild('tickerContainer') tickerContainer!: ElementRef;
ngAfterViewInit(): void {
autoAnimate(this.tickerContainer.nativeElement);
}
}