feat(blackjack): add action indicators and loading states
This commit is contained in:
parent
d2b22b561d
commit
acdbea5a99
7 changed files with 208 additions and 27 deletions
|
@ -1,6 +1,7 @@
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input, AfterViewInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { suitSymbols, Suit } from '../../models/blackjack.model';
|
||||
import { gsap } from 'gsap';
|
||||
|
||||
@Component({
|
||||
selector: 'app-playing-card',
|
||||
|
@ -8,31 +9,90 @@ import { suitSymbols, Suit } from '../../models/blackjack.model';
|
|||
imports: [CommonModule],
|
||||
template: `
|
||||
<div
|
||||
class="w-24 h-36 rounded-lg p-2 relative flex flex-col justify-between shadow-lg"
|
||||
#cardElement
|
||||
class="w-24 h-36 rounded-lg p-2 relative flex flex-col justify-between shadow-lg card-element"
|
||||
[class]="hidden ? 'bg-red-800' : 'bg-white'"
|
||||
>
|
||||
@if (!hidden) {
|
||||
<span class="text-xl font-bold text-accent-red">{{ getDisplayRank(rank) }}</span>
|
||||
<span class="text-xl font-bold" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{ getDisplayRank(rank) }}</span>
|
||||
}
|
||||
@if (!hidden) {
|
||||
<span
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-3xl text-accent-red"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-3xl"
|
||||
[class]="isRedSuit ? 'text-accent-red' : 'text-black'"
|
||||
>{{ getSuitSymbol(suit) }}</span
|
||||
>
|
||||
}
|
||||
@if (!hidden) {
|
||||
<span class="text-xl font-bold text-accent-red self-end rotate-180">{{
|
||||
<span class="text-xl font-bold self-end rotate-180" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{
|
||||
getDisplayRank(rank)
|
||||
}}</span>
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.card-element {
|
||||
transform-style: preserve-3d;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PlayingCardComponent {
|
||||
export class PlayingCardComponent implements AfterViewInit, OnChanges {
|
||||
@Input({ required: true }) rank!: string;
|
||||
@Input({ required: true }) suit!: Suit;
|
||||
@Input({ required: true }) hidden!: boolean;
|
||||
@Input() isNew: boolean = false;
|
||||
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
|
||||
get isRedSuit(): boolean {
|
||||
return this.suit === 'HEARTS' || this.suit === 'DIAMONDS';
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (this.isNew) {
|
||||
this.animateNewCard();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['hidden'] && !changes['hidden'].firstChange) {
|
||||
this.animateCardFlip();
|
||||
}
|
||||
}
|
||||
|
||||
private animateNewCard(): void {
|
||||
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
||||
gsap.fromTo(
|
||||
cardElement,
|
||||
{
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
rotation: -10,
|
||||
scale: 0.7
|
||||
},
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
rotation: 0,
|
||||
scale: 1,
|
||||
duration: 0.5,
|
||||
ease: 'power2.out'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private animateCardFlip(): void {
|
||||
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
||||
gsap.to(cardElement, {
|
||||
rotationY: 180,
|
||||
duration: 0.3,
|
||||
onComplete: () => {
|
||||
gsap.set(cardElement, { rotationY: 0 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected getSuitSymbol(suit: Suit): string {
|
||||
return suitSymbols[suit];
|
||||
|
|
Reference in a new issue