feat: gameboard ui
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="flex justify-center items-end relative h-40">
|
||||
<TransitionGroup
|
||||
enter-active-class="transition-all duration-500 ease-out"
|
||||
enter-from-class="opacity-0 -translate-y-8 scale-75"
|
||||
enter-to-class="opacity-100 translate-y-0 scale-100"
|
||||
>
|
||||
<div
|
||||
v-for="(card, index) in cards"
|
||||
:key="`${card.suit}-${card.rank}-${index}`"
|
||||
class="absolute transition-all duration-300 ease-out"
|
||||
:style="getCardStyle(index)"
|
||||
@mouseenter="hoveredIndex = index"
|
||||
@mouseleave="hoveredIndex = null"
|
||||
@click="$emit('card-clicked', { card, index })"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'transition-all duration-300',
|
||||
isPlayable(index) && 'ring-2 ring-yellow-400 ring-offset-2 rounded-lg',
|
||||
hoveredIndex === index && 'scale-110 -translate-y-4 z-50',
|
||||
]"
|
||||
>
|
||||
<GameCard :suit="card.suit" :rank="card.rank" :face-down="faceDown" />
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import GameCard from './GameCard.vue'
|
||||
|
||||
const props = defineProps({
|
||||
cards: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
faceDown: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxCards: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
validator: (value) => [3, 9].includes(value),
|
||||
},
|
||||
playableCards: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
defineEmits(['card-clicked'])
|
||||
|
||||
const hoveredIndex = ref(null)
|
||||
|
||||
// Calculate overlap percentage based on max cards
|
||||
// 3 cards: 70% overlap, 9 cards: 85% overlap
|
||||
const overlapPercentage = computed(() => {
|
||||
return props.maxCards === 3 ? 0.7 : 0.85
|
||||
})
|
||||
|
||||
// Calculate card spacing dynamically
|
||||
const cardWidth = 128 // w-32 from GameCard component
|
||||
const getCardStyle = (index) => {
|
||||
const totalCards = props.cards.length
|
||||
const overlapOffset = cardWidth * (1 - overlapPercentage.value)
|
||||
|
||||
// Calculate total width needed
|
||||
const totalWidth = cardWidth + (totalCards - 1) * overlapOffset
|
||||
const startOffset = -totalWidth / 2 + cardWidth / 2
|
||||
|
||||
// Base position
|
||||
let left = startOffset + index * overlapOffset
|
||||
|
||||
// If a card is hovered, shift neighbors
|
||||
if (hoveredIndex.value !== null && hoveredIndex.value !== index) {
|
||||
if (index < hoveredIndex.value) {
|
||||
left -= 10 // Push left cards more to the left
|
||||
} else if (index > hoveredIndex.value) {
|
||||
left += 10 // Push right cards more to the right
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
left: `calc(50% + ${left}px)`,
|
||||
zIndex: hoveredIndex.value === index ? 50 : index + 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a card is playable
|
||||
const isPlayable = (index) => {
|
||||
return props.playableCards.includes(index)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user