171 lines
4.1 KiB
Vue
171 lines
4.1 KiB
Vue
<script lang="ts" setup>
|
|
import {useWebSocket} from "@/composables/useWebsocket.ts";
|
|
import {useIngame} from "@/composables/useIngame.ts";
|
|
import type {GameInfo} from "@/types/GameTypes.ts";
|
|
import {useQuasar} from "quasar";
|
|
import { ref } from 'vue';
|
|
|
|
const wb = useWebSocket()
|
|
const wi = useIngame()
|
|
const $q = useQuasar();
|
|
|
|
const wiggleIdx = ref<number | null>(null)
|
|
let wiggleTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
function triggerWiggle(index: number) {
|
|
// clear previous timer if any
|
|
if (wiggleTimer) clearTimeout(wiggleTimer)
|
|
wiggleIdx.value = index
|
|
wiggleTimer = setTimeout(() => {
|
|
wiggleIdx.value = null
|
|
wiggleTimer = null
|
|
}, 700)
|
|
}
|
|
|
|
function handlePlayCard(index: number | null) {
|
|
if (index === null) return
|
|
|
|
wb.sendAndWait((<GameInfo>wi.data)?.self?.dogLife ? "PlayDogCard" : "PlayCard", { cardindex: index }).catch((error) => {
|
|
triggerWiggle(index)
|
|
$q.notify({
|
|
message: error.message,
|
|
color: "negative",
|
|
position: "top"
|
|
})
|
|
|
|
})
|
|
}
|
|
function onBeforeLeave(el: Element) {
|
|
const element = el as HTMLElement;
|
|
const { marginLeft, marginTop, width, height } = window.getComputedStyle(element);
|
|
element.style.left = `${element.offsetLeft - parseFloat(marginLeft)}px`;
|
|
element.style.top = `${element.offsetTop - parseFloat(marginTop)}px`;
|
|
element.style.width = width;
|
|
element.style.height = height;
|
|
}
|
|
function handleSkipDogLife() {
|
|
wb.sendAndWait("PlayDogCard", { cardindex: 'Skip' }).catch((error) => {
|
|
$q.notify({
|
|
message: error.message,
|
|
color: "negative",
|
|
position: "top"
|
|
})
|
|
})
|
|
}
|
|
|
|
function getCardImagePath(cardPath: string) {
|
|
if (!cardPath) return ''
|
|
if (cardPath.includes('://') || cardPath.startsWith('/')) return cardPath
|
|
return `/${cardPath}`
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="hand-container">
|
|
<div id="card-slide" class="ingame-cards-slide">
|
|
<transition-group
|
|
tag="div"
|
|
class="cards-row"
|
|
name="card-move"
|
|
enter-active-class="animate__animated animate__fadeIn"
|
|
@before-leave="onBeforeLeave"
|
|
move-class="card-moving">
|
|
<div v-for="card in (<GameInfo>wi.data)?.hand?.cards" :key="card.identifier" :class="['handcard', { wiggle: wiggleIdx === card.idx }]">
|
|
<div class="card-btn" aria-label="Play card">
|
|
<q-img :src="getCardImagePath(card.path)" v-on:click="handlePlayCard(card.idx)" :alt="card.identifier" class="card" />
|
|
</div>
|
|
</div>
|
|
</transition-group>
|
|
<div v-if="(<GameInfo>wi.data)?.self?.dogLife" class="dog-actions">
|
|
<q-btn color="negative" label="Skip Turn" @click="handleSkipDogLife" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.bottom-div {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
max-width: 1400px;
|
|
width: 100%;
|
|
margin: 0;
|
|
text-align: center;
|
|
padding: 10px;
|
|
}
|
|
.hand-container {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
.ingame-cards-slide {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.cards-row {
|
|
display: flex;
|
|
flex-wrap: nowrap;
|
|
gap: 8px;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
}
|
|
.handcard {
|
|
border-radius: 6px;
|
|
}
|
|
.card-btn {
|
|
padding: 0;
|
|
min-width: 0;
|
|
border-radius: 6px;
|
|
}
|
|
.inactive {
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
.dog-actions {
|
|
margin-top: 8px;
|
|
}
|
|
.handcard :hover {
|
|
box-shadow: 3px 3px 3px #000000;
|
|
}
|
|
.card {
|
|
width:120px;
|
|
border-radius:6px
|
|
}
|
|
.wiggle {
|
|
animation: wiggle 700ms ease-in-out;
|
|
}
|
|
|
|
@keyframes wiggle {
|
|
0% { transform: translateY(0) rotate(0deg); }
|
|
15% { transform: translateY(-8px) rotate(-6deg); }
|
|
35% { transform: translateY(0) rotate(6deg); }
|
|
55% { transform: translateY(-4px) rotate(-3deg); }
|
|
75% { transform: translateY(0) rotate(2deg); }
|
|
100% { transform: translateY(0) rotate(0deg); }
|
|
}
|
|
|
|
@media (max-height: 500px) {
|
|
.card {
|
|
width: 80px;
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
.card-moving {
|
|
transition: transform 0.5s ease;
|
|
transition-delay: 1s;
|
|
}
|
|
.card-move-leave-active {
|
|
position: absolute;
|
|
transition: all 0.5s ease;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card-move-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-400px);
|
|
}
|
|
</style>
|