From 50eb399fe2c3dd866f3fbf0a09a7d959e6dca88e Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 5 Feb 2025 14:40:15 +0100 Subject: [PATCH] feat: implement animated buttons and winner ticker component --- .../src/app/landing/landing.component.html | 164 ++---------------- frontend/src/app/landing/landing.component.ts | 6 +- .../animated-button.component.ts | 48 +++++ .../game-card/game-card.component.ts | 98 +++++++++++ .../winner-ticker/winner-ticker.component.ts | 32 ++++ 5 files changed, 202 insertions(+), 146 deletions(-) create mode 100644 frontend/src/app/shared/components/animated-button/animated-button.component.ts create mode 100644 frontend/src/app/shared/components/game-card/game-card.component.ts create mode 100644 frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts diff --git a/frontend/src/app/landing/landing.component.html b/frontend/src/app/landing/landing.component.html index 5922ccc..aeaa65c 100644 --- a/frontend/src/app/landing/landing.component.html +++ b/frontend/src/app/landing/landing.component.html @@ -6,20 +6,7 @@ [class.py-1.5]="!isScrolled" >
-
- - 🎰 - - {{ winner.name }} - {{ winner.isVIP ? '(VIP)' : '' }} - won €{{ winner.amount | number }} - ({{ winner.multiplier }}x) - - -
+
@@ -60,15 +47,7 @@ (78.9% Win Rate) - + START PLAYING @@ -157,17 +136,9 @@
- + + CLAIM YOUR €10,000 NOW +
@@ -190,25 +161,7 @@
-
-
- - - 🎰 {{ winner.name }} - {{ - winner.isVIP ? '(VIP)' : '' - }} - turned €{{ winner.betAmount }} into - €{{ winner.amount | number }} - ({{ winner.multiplier }}x) - - -
-
+
@@ -220,79 +173,12 @@
-
-
- -
-
-
-

{{ game.name }}

-
- HOT 🔥 - {{ game.lastWinner }} won €{{ game.lastWin | number }} -
-
-

{{ game.description }}

-
-
- - {{ game.winChance }}% Win Rate - - Max Win: €{{ game.maxWin | number }} -
-
- Min: €{{ game.minBet }} | Max: €{{ game.maxBet }} -
- Popularity: -
-
-
-
-
-
-
- - {{ feature }} - -
- -
-
-
-
-
+
@@ -396,17 +282,9 @@ > Maybe later - + + {{ popup.cta }} +
- + SPIN AGAIN + diff --git a/frontend/src/app/landing/landing.component.ts b/frontend/src/app/landing/landing.component.ts index 4aceb54..1f0ff86 100644 --- a/frontend/src/app/landing/landing.component.ts +++ b/frontend/src/app/landing/landing.component.ts @@ -22,10 +22,14 @@ import { WinnerService } from '../services/winner.service'; import { JackpotService } from '../services/jackpot.service'; import { AnimationService } from '../services/animation.service'; +import { AnimatedButtonComponent } from '../shared/components/animated-button/animated-button.component'; +import { WinnerTickerComponent } from '../shared/components/winner-ticker/winner-ticker.component'; +import { GameCardComponent } from '../shared/components/game-card/game-card.component'; + @Component({ selector: 'app-landing', standalone: true, - imports: [CommonModule], + imports: [CommonModule, AnimatedButtonComponent, WinnerTickerComponent, GameCardComponent], templateUrl: './landing.component.html', changeDetection: ChangeDetectionStrategy.OnPush, animations: [ diff --git a/frontend/src/app/shared/components/animated-button/animated-button.component.ts b/frontend/src/app/shared/components/animated-button/animated-button.component.ts new file mode 100644 index 0000000..959ef17 --- /dev/null +++ b/frontend/src/app/shared/components/animated-button/animated-button.component.ts @@ -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: ` + + `, + styles: [], +}) +export class AnimatedButtonComponent { + @Input() variant: 'primary' | 'secondary' = 'primary'; + @Input() size: 'normal' | 'large' = 'normal'; + @Output() buttonClick = new EventEmitter(); + + 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); + } +} diff --git a/frontend/src/app/shared/components/game-card/game-card.component.ts b/frontend/src/app/shared/components/game-card/game-card.component.ts new file mode 100644 index 0000000..7c8778a --- /dev/null +++ b/frontend/src/app/shared/components/game-card/game-card.component.ts @@ -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: ` +
+
+ +
+
+
+

{{ game.name }}

+
+ + HOT 🔥 + + + {{ game.lastWinner }} won €{{ game.lastWin | number }} + +
+
+

{{ game.description }}

+
+
+ + {{ game.winChance }}% Win Rate + + Max Win: €{{ game.maxWin | number }} +
+
+ + Min: €{{ game.minBet }} | Max: €{{ game.maxBet }} + +
+ Popularity: +
+
+
+
+
+
+
+ + {{ feature }} + +
+ PLAY NOW +
+
+
+
+
+ `, + styles: [], +}) +export class GameCardComponent { + @Input() game!: Game; + @Output() play = new EventEmitter(); + + 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(); + } +} diff --git a/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts b/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts new file mode 100644 index 0000000..c22e661 --- /dev/null +++ b/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts @@ -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: ` +
+ + 🎰 + + {{ winner.name }} + {{ winner.isVIP ? '(VIP)' : '' }} + won €{{ winner.amount | number }} + ({{ winner.multiplier }}x) + + +
+ `, + styles: [], +}) +export class WinnerTickerComponent implements AfterViewInit { + @Input() winners: Winner[] = []; + @ViewChild('tickerContainer') tickerContainer!: ElementRef; + + ngAfterViewInit(): void { + autoAnimate(this.tickerContainer.nativeElement); + } +}