diff --git a/frontend/bun.lock b/frontend/bun.lock
index f4f97b2..d5cae8d 100644
--- a/frontend/bun.lock
+++ b/frontend/bun.lock
@@ -19,6 +19,7 @@
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@stripe/stripe-js": "^5.6.0",
"@tailwindcss/postcss": "^4.0.3",
+ "countup.js": "^2.8.0",
"gsap": "^3.12.7",
"keycloak-angular": "^16.0.1",
"keycloak-js": "^25.0.5",
@@ -853,6 +854,8 @@
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
+ "countup.js": ["countup.js@2.8.0", "", {}, "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="],
+
"critters": ["critters@0.0.24", "", { "dependencies": { "chalk": "^4.1.0", "css-select": "^5.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.2", "htmlparser2": "^8.0.2", "postcss": "^8.4.23", "postcss-media-query-parser": "^0.2.3" } }, "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
diff --git a/frontend/package.json b/frontend/package.json
index d83ed30..bcc969a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -28,6 +28,7 @@
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@stripe/stripe-js": "^5.6.0",
"@tailwindcss/postcss": "^4.0.3",
+ "countup.js": "^2.8.0",
"gsap": "^3.12.7",
"keycloak-angular": "^16.0.1",
"keycloak-js": "^25.0.5",
diff --git a/frontend/src/app/feature/game/blackjack/blackjack.component.html b/frontend/src/app/feature/game/blackjack/blackjack.component.html
index 93328a3..aa7426a 100644
--- a/frontend/src/app/feature/game/blackjack/blackjack.component.html
+++ b/frontend/src/app/feature/game/blackjack/blackjack.component.html
@@ -6,19 +6,6 @@
- @if (isActionInProgress()) {
-
-
-
-
{{ currentAction() }}
-
-
- }
-
@if (gameInProgress()) {
('');
showDebtDialog = signal(false);
debtAmount = signal(0);
@@ -96,7 +95,6 @@ export default class BlackjackComponent implements OnInit {
onNewGame(bet: number): void {
this.isActionInProgress.set(true);
- this.currentAction.set('Spiel wird gestartet...');
this.blackjackService.startGame(bet).subscribe({
next: (game) => {
@@ -115,7 +113,6 @@ export default class BlackjackComponent implements OnInit {
if (!this.currentGameId() || this.isActionInProgress()) return;
this.isActionInProgress.set(true);
- this.currentAction.set('Karte wird gezogen...');
this.blackjackService.hit(this.currentGameId()!).subscribe({
next: (game) => {
@@ -142,7 +139,6 @@ export default class BlackjackComponent implements OnInit {
}
this.isActionInProgress.set(true);
- this.currentAction.set('Dealer zieht Karten...');
this.blackjackService.stand(this.currentGameId()!).subscribe({
next: (game) => {
@@ -167,7 +163,6 @@ export default class BlackjackComponent implements OnInit {
}
this.isActionInProgress.set(true);
- this.currentAction.set('Einsatz wird verdoppelt...');
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
next: (game) => {
diff --git a/frontend/src/app/feature/game/blackjack/components/animated-number/animated-number.component.ts b/frontend/src/app/feature/game/blackjack/components/animated-number/animated-number.component.ts
new file mode 100644
index 0000000..aa4c1b6
--- /dev/null
+++ b/frontend/src/app/feature/game/blackjack/components/animated-number/animated-number.component.ts
@@ -0,0 +1,80 @@
+import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
+import { CommonModule, CurrencyPipe } from '@angular/common';
+import { CountUp } from 'countup.js';
+
+@Component({
+ selector: 'app-animated-number',
+ standalone: true,
+ imports: [CommonModule, CurrencyPipe],
+ template: `
+ {{ formattedValue }}
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AnimatedNumberComponent implements OnChanges, AfterViewInit {
+ @Input() value = 0;
+ @Input() duration = 1;
+ @Input() ease = 'power1.out';
+
+ @ViewChild('numberElement') numberElement!: ElementRef;
+
+ private countUp: CountUp | null = null;
+ private previousValue = 0;
+ formattedValue = '0,00 €';
+
+ ngAfterViewInit(): void {
+ this.initializeCountUp();
+ if (this.countUp && this.value !== 0) {
+ this.countUp.start(() => {
+ this.previousValue = this.value;
+ });
+ }
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes['value']) {
+ if (this.countUp) {
+ const startVal = this.previousValue;
+ const endVal = this.value;
+
+ // Update the CountUp instance with new start and end values
+ this.countUp.update(endVal);
+ this.previousValue = endVal;
+ } else {
+ // Format the initial value if CountUp is not yet initialized
+ this.formattedValue = new Intl.NumberFormat('de-DE', {
+ style: 'currency',
+ currency: 'EUR',
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ }).format(this.value);
+ }
+ }
+ }
+
+ private initializeCountUp(): void {
+ if (this.numberElement) {
+ this.countUp = new CountUp(this.numberElement.nativeElement, this.value, {
+ startVal: this.previousValue,
+ duration: this.duration,
+ easingFn: (t, b, c, d) => {
+ // Custom easing function based on the input ease type
+ if (this.ease === 'power1.out') {
+ return c * (1 - Math.pow(1 - t / d, 1)) + b;
+ }
+ return c * (t / d) + b; // linear fallback
+ },
+ formattingFn: (value) => {
+ const formatted = new Intl.NumberFormat('de-DE', {
+ style: 'currency',
+ currency: 'EUR',
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ }).format(value);
+ this.formattedValue = formatted;
+ return formatted;
+ },
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/feature/game/blackjack/components/game-controls/game-controls.component.ts b/frontend/src/app/feature/game/blackjack/components/game-controls/game-controls.component.ts
index d01adc6..9799b08 100644
--- a/frontend/src/app/feature/game/blackjack/components/game-controls/game-controls.component.ts
+++ b/frontend/src/app/feature/game/blackjack/components/game-controls/game-controls.component.ts
@@ -28,54 +28,27 @@ import { GameControlsService } from '@blackjack/services/game-controls.service';
(click)="hit.emit()"
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px] relative"
[disabled]="gameState !== GameState.IN_PROGRESS || isActionInProgress"
- [class.opacity-50]="isActionInProgress"
>
- Ziehen
- @if (isActionInProgress) {
-
- }
+ Ziehen
- Halten
- @if (isActionInProgress) {
-
- }
+ Halten
- Verdoppeln
- @if (isActionInProgress) {
-
- }
+ Verdoppeln
Abbrechen
@@ -97,4 +70,12 @@ export class GameControlsComponent {
protected readonly GameState = GameState;
constructor(protected gameControlsService: GameControlsService) {}
+
+ get canDoubleDown(): boolean {
+ return (
+ this.gameState === GameState.IN_PROGRESS &&
+ this.playerCards.length === 2 &&
+ !this.isActionInProgress
+ );
+ }
}
diff --git a/frontend/src/app/feature/game/blackjack/components/game-info/game-info.component.ts b/frontend/src/app/feature/game/blackjack/components/game-info/game-info.component.ts
index df29e81..ecd1fad 100644
--- a/frontend/src/app/feature/game/blackjack/components/game-info/game-info.component.ts
+++ b/frontend/src/app/feature/game/blackjack/components/game-info/game-info.component.ts
@@ -11,11 +11,12 @@ import {
import { CommonModule, CurrencyPipe } from '@angular/common';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { BettingService } from '@blackjack/services/betting.service';
+import { AnimatedNumberComponent } from '../animated-number/animated-number.component';
@Component({
selector: 'app-game-info',
standalone: true,
- imports: [CommonModule, CurrencyPipe, ReactiveFormsModule],
+ imports: [CommonModule, CurrencyPipe, ReactiveFormsModule, AnimatedNumberComponent],
template: `
Spiel Informationen
@@ -23,7 +24,7 @@ import { BettingService } from '@blackjack/services/betting.service';
Aktuelle Wette:
0 ? 'text-accent-red' : 'text-text-secondary'">
- {{ currentBet | currency: 'EUR' }}
+
diff --git a/frontend/src/app/feature/game/blackjack/components/game-result/game-result.component.ts b/frontend/src/app/feature/game/blackjack/components/game-result/game-result.component.ts
index 63ca955..5547e27 100644
--- a/frontend/src/app/feature/game/blackjack/components/game-result/game-result.component.ts
+++ b/frontend/src/app/feature/game/blackjack/components/game-result/game-result.component.ts
@@ -2,11 +2,12 @@ import { ChangeDetectionStrategy, Component, Input, Output, EventEmitter } from
import { CommonModule, CurrencyPipe } from '@angular/common';
import { animate, style, transition, trigger } from '@angular/animations';
import { GameState } from '../../enum/gameState';
+import { AnimatedNumberComponent } from '../animated-number/animated-number.component';
@Component({
selector: 'app-game-result',
standalone: true,
- imports: [CommonModule, CurrencyPipe],
+ imports: [CommonModule, CurrencyPipe, AnimatedNumberComponent],
template: `
@@ -18,7 +19,9 @@ import { GameState } from '../../enum/gameState';
>
Einsatz:
-
{{ amount | currency: 'EUR' }}
+
{{ isDraw ? 'Zurückgegeben:' : isWin ? 'Gewonnen:' : 'Verloren:' }}
@@ -31,9 +34,13 @@ import { GameState } from '../../enum/gameState';
'text-yellow-400': isDraw,
}"
>
- {{ isLoss ? '-' : '+' }}{{ isWin ? amount * 2 : (amount | currency: 'EUR') }}
+ {{ isLoss ? '-' : '+' }}
+
- (Einsatz {{ amount | currency: 'EUR' }} × 2)
+ (Einsatz
× 2)
@@ -41,7 +48,7 @@ import { GameState } from '../../enum/gameState';
Kontostand:
- {{ balance | currency: 'EUR' }}
+
diff --git a/frontend/src/app/shared/components/debt-dialog/debt-dialog.component.ts b/frontend/src/app/shared/components/debt-dialog/debt-dialog.component.ts
index dd60fdc..a97f431 100644
--- a/frontend/src/app/shared/components/debt-dialog/debt-dialog.component.ts
+++ b/frontend/src/app/shared/components/debt-dialog/debt-dialog.component.ts
@@ -11,18 +11,19 @@ import {
import { CommonModule } from '@angular/common';
import { animate, style, transition, trigger } from '@angular/animations';
import { interval, Subscription, takeWhile } from 'rxjs';
+import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
@Component({
selector: 'app-debt-dialog',
standalone: true,
- imports: [CommonModule],
+ imports: [CommonModule, AnimatedNumberComponent],
template: `
WARNUNG!
Du hast nicht genug Geld für den Double Down. Du bist jetzt im Minus und schuldest uns
- {{ amount | currency: 'EUR' }}.
+ .
Liefer das Geld sofort an den Dead Drop oder es wird unangenehme Konsequenzen geben!
@@ -32,7 +33,9 @@ import { interval, Subscription, takeWhile } from 'rxjs';
>
Schulden:
-
{{ amount | currency: 'EUR' }}
+
diff --git a/frontend/src/app/shared/components/navbar/navbar.component.html b/frontend/src/app/shared/components/navbar/navbar.component.html
index b34aea5..e9f8e91 100644
--- a/frontend/src/app/shared/components/navbar/navbar.component.html
+++ b/frontend/src/app/shared/components/navbar/navbar.component.html
@@ -19,9 +19,9 @@
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"
routerLink="/home"
>
-
{{
- balance() | currency: 'EUR' : 'symbol' : '1.2-2'
- }}
+
+
+
Abmelden
}
diff --git a/frontend/src/app/shared/components/navbar/navbar.component.ts b/frontend/src/app/shared/components/navbar/navbar.component.ts
index 6b972ac..f1fffba 100644
--- a/frontend/src/app/shared/components/navbar/navbar.component.ts
+++ b/frontend/src/app/shared/components/navbar/navbar.component.ts
@@ -11,12 +11,13 @@ import { KeycloakService } from 'keycloak-angular';
import { CurrencyPipe } from '@angular/common';
import { UserService } from '@service/user.service';
import { Subscription } from 'rxjs';
+import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
standalone: true,
- imports: [RouterModule, CurrencyPipe],
+ imports: [RouterModule, CurrencyPipe, AnimatedNumberComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavbarComponent implements OnInit, OnDestroy {