evilSpins v1
This commit is contained in:
190
app/pages/dev.vue
Normal file
190
app/pages/dev.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div>
|
||||
<button
|
||||
class="fixed bottom-4 right-4 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full shadow-lg z-50 transition-colors"
|
||||
title="Ranger les cartes"
|
||||
@click="arrangeCards"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16m-7 6h7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div ref="deck" class="deck">
|
||||
<card
|
||||
v-for="track in tracks"
|
||||
:key="track.id"
|
||||
:track="track"
|
||||
:class="['card', 'id-' + track.id, { dragging: dragging === track }]"
|
||||
:style="{
|
||||
top: track.y + 'px',
|
||||
left: track.x + 'px',
|
||||
zIndex: track.zIndex || 1,
|
||||
transform: `rotate(${track.rotation || 0}deg)`
|
||||
}"
|
||||
:is-face-up="track.isFaceUp"
|
||||
@mousedown="startDrag($event, track)"
|
||||
@click="flipCard(track)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useDataStore } from '~/store/data'
|
||||
|
||||
const tracks = ref([])
|
||||
const deck = ref(null)
|
||||
let zIndexCounter = 1
|
||||
|
||||
let dragging = null
|
||||
let offset = { x: 0, y: 0 }
|
||||
let velocity = { x: 0, y: 0 }
|
||||
let lastTime = 0
|
||||
|
||||
const dataStore = useDataStore()
|
||||
|
||||
definePageMeta({ layout: 'default' })
|
||||
|
||||
onMounted(async () => {
|
||||
await dataStore.loadData()
|
||||
tracks.value = dataStore.getTracksByboxId('ES2025').map((t, i) => ({
|
||||
...t,
|
||||
x: t.x ?? 50 + i * 20,
|
||||
y: t.y ?? 50 + i * 20,
|
||||
zIndex: t.zIndex ?? i + 1,
|
||||
rotation: 0,
|
||||
isFaceUp: t.isFaceUp ?? false
|
||||
}))
|
||||
|
||||
window.addEventListener('mousemove', onDrag)
|
||||
window.addEventListener('mouseup', stopDrag)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('mousemove', onDrag)
|
||||
window.removeEventListener('mouseup', stopDrag)
|
||||
})
|
||||
|
||||
function startDrag(e, track) {
|
||||
dragging = track
|
||||
const rect = deck.value.getBoundingClientRect()
|
||||
offset.x = e.clientX - rect.left - track.x
|
||||
offset.y = e.clientY - rect.top - track.y
|
||||
lastTime = performance.now()
|
||||
velocity = { x: 0, y: 0 }
|
||||
|
||||
zIndexCounter += 1
|
||||
track.zIndex = zIndexCounter
|
||||
}
|
||||
|
||||
function onDrag(e) {
|
||||
if (!dragging) return
|
||||
|
||||
const rect = deck.value.getBoundingClientRect()
|
||||
const newX = e.clientX - rect.left - offset.x
|
||||
const newY = e.clientY - rect.top - offset.y
|
||||
|
||||
const now = performance.now()
|
||||
const dt = now - lastTime
|
||||
velocity.x = (newX - dragging.x) / dt
|
||||
velocity.y = (newY - dragging.y) / dt
|
||||
lastTime = now
|
||||
|
||||
dragging.x = newX
|
||||
dragging.y = newY
|
||||
|
||||
// Rotation dynamique selon la position horizontale
|
||||
const centerX = rect.width / 2
|
||||
const dx = dragging.x - centerX
|
||||
dragging.rotation = dx * 0.05
|
||||
}
|
||||
|
||||
function stopDrag() {
|
||||
if (!dragging) return
|
||||
const track = dragging
|
||||
dragging = null
|
||||
|
||||
// Inertie douce
|
||||
const decay = 0.95
|
||||
function animateInertia() {
|
||||
if (Math.abs(velocity.x) < 0.02 && Math.abs(velocity.y) < 0.02) return
|
||||
track.x += velocity.x * 16
|
||||
track.y += velocity.y * 16
|
||||
velocity.x *= decay
|
||||
velocity.y *= decay
|
||||
requestAnimationFrame(animateInertia)
|
||||
}
|
||||
animateInertia()
|
||||
}
|
||||
|
||||
function flipCard(track) {
|
||||
track.isFaceUp = true
|
||||
}
|
||||
|
||||
function arrangeCards() {
|
||||
const deckRect = deck.value.getBoundingClientRect()
|
||||
const cardWidth = 224
|
||||
const cardHeight = 320
|
||||
const padding = 20
|
||||
const cardsPerRow = Math.max(
|
||||
1,
|
||||
Math.floor((deckRect.width - padding * 2) / (cardWidth + padding))
|
||||
)
|
||||
|
||||
tracks.value.forEach((track, index) => {
|
||||
const row = Math.floor(index / cardsPerRow)
|
||||
const col = index % cardsPerRow
|
||||
|
||||
track.x = padding + col * (cardWidth + padding)
|
||||
track.y = padding + row * (cardHeight / 3)
|
||||
track.zIndex = index + 1
|
||||
track.rotation = 0
|
||||
|
||||
zIndexCounter = Math.max(zIndexCounter, track.zIndex)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.deck {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 80vh;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: absolute;
|
||||
cursor: grab;
|
||||
transition:
|
||||
transform 0.1s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.card.dragging {
|
||||
z-index: 999;
|
||||
cursor: grabbing;
|
||||
transform: scale(1.05) rotate(0deg);
|
||||
/* rotation sera remplacée dynamiquement */
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user