add working player
All checks were successful
Deploy App / build (push) Successful in 1m20s
Deploy App / deploy (push) Successful in 16s

This commit is contained in:
valere
2025-10-04 00:49:12 +02:00
parent fef1a8c234
commit 96ffb4b10a
9 changed files with 242 additions and 153 deletions

View File

@@ -31,6 +31,8 @@ const props = withDefaults(
{ BoxState: 'list' }
)
const isDraggable = computed(() => !['list', 'hidden'].includes(BoxState.value()))
// --- Réfs ---
const scene = ref<HTMLElement>()
const box = ref<HTMLElement>()
@@ -123,61 +125,81 @@ function tickInertia() {
}
// --- Pointer events ---
let listenersAttached = false
const down = (ev: PointerEvent) => {
ev.preventDefault()
dragging = true
box.value?.setPointerCapture(ev.pointerId)
lastPointer = { x: ev.clientX, y: ev.clientY, time: performance.now() }
velocity = { x: 0, y: 0 }
if (raf) { cancelAnimationFrame(raf); raf = null }
}
const move = (ev: PointerEvent) => {
if (!dragging) return
ev.preventDefault()
const now = performance.now()
const dx = ev.clientX - lastPointer.x
const dy = ev.clientY - lastPointer.y
const dt = Math.max(1, now - lastPointer.time)
rotateY.value += dx * sensitivity
rotateX.value -= dy * sensitivity
rotateX.value = Math.max(-80, Math.min(80, rotateX.value))
velocity.x = (dx / dt) * 16 * sensitivity
velocity.y = (-dy / dt) * 16 * sensitivity
lastPointer = { x: ev.clientX, y: ev.clientY, time: now }
applyTransform(0) // immédiat pendant drag
}
const end = (ev: PointerEvent) => {
if (!dragging) return
dragging = false
try { box.value?.releasePointerCapture(ev.pointerId) } catch { }
if (enableInertia && (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity)) {
if (!raf) raf = requestAnimationFrame(tickInertia)
}
}
function addListeners() {
if (!box.value || listenersAttached) return
box.value.addEventListener('pointerdown', down)
box.value.addEventListener('pointermove', move)
box.value.addEventListener('pointerup', end)
box.value.addEventListener('pointercancel', end)
box.value.addEventListener('pointerleave', end)
listenersAttached = true
}
function removeListeners() {
if (!box.value || !listenersAttached) return
box.value.removeEventListener('pointerdown', down)
box.value.removeEventListener('pointermove', move)
box.value.removeEventListener('pointerup', end)
box.value.removeEventListener('pointercancel', end)
box.value.removeEventListener('pointerleave', end)
listenersAttached = false
}
onMounted(() => {
applyColor()
applyBoxState()
if (isDraggable) addListeners()
})
const down = (ev: PointerEvent) => {
ev.preventDefault()
dragging = true
box.value?.setPointerCapture(ev.pointerId)
lastPointer = { x: ev.clientX, y: ev.clientY, time: performance.now() }
velocity = { x: 0, y: 0 }
if (raf) { cancelAnimationFrame(raf); raf = null }
}
const move = (ev: PointerEvent) => {
if (!dragging) return
ev.preventDefault()
const now = performance.now()
const dx = ev.clientX - lastPointer.x
const dy = ev.clientY - lastPointer.y
const dt = Math.max(1, now - lastPointer.time)
rotateY.value += dx * sensitivity
rotateX.value -= dy * sensitivity
rotateX.value = Math.max(-80, Math.min(80, rotateX.value))
velocity.x = (dx / dt) * 16 * sensitivity
velocity.y = (-dy / dt) * 16 * sensitivity
lastPointer = { x: ev.clientX, y: ev.clientY, time: now }
applyTransform(0) // immédiat pendant drag
}
const end = (ev: PointerEvent) => {
if (!dragging) return
dragging = false
try { box.value?.releasePointerCapture(ev.pointerId) } catch { }
if (enableInertia && (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity)) {
if (!raf) raf = requestAnimationFrame(tickInertia)
}
}
box.value?.addEventListener('pointerdown', down)
box.value?.addEventListener('pointermove', move)
box.value?.addEventListener('pointerup', end)
box.value?.addEventListener('pointercancel', end)
box.value?.addEventListener('pointerleave', end)
onBeforeUnmount(() => {
cancelAnimationFrame(raf!)
})
onBeforeUnmount(() => {
cancelAnimationFrame(raf!)
removeListeners()
})
// --- Watchers ---
watch(() => props.BoxState, () => applyBoxState())
watch(() => props.compilation, () => applyColor(), { deep: true })
watch(() => isDraggable, (enabled) => enabled ? addListeners() : removeListeners())
</script>
<style lang="scss" scoped>

View File

@@ -1,5 +1,5 @@
<template>
<article class="flip-card w-56 h-80" :data-flipped="props.isflipped">
<article class="flip-card w-56 h-80">
<div class="flip-inner">
<main
class="flip-front backdrop-blur-sm border-2 -mt-12 z-10 card w-56 h-80 p-3 bg-opacity-40 hover:bg-opacity-80 hover:shadow-xl transition-all bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden">
@@ -39,7 +39,7 @@
import type { Track } from '~~/types/types'
import { usePlayerStore } from '~/store/player'
const props = defineProps<{ track: Track, isflipped: false }>()
const props = defineProps<{ track: Track }>()
const playerStore = usePlayerStore()
const coverUrl = props.track.coverId.startsWith('http')
? props.track.coverId
@@ -66,7 +66,7 @@ const coverUrl = props.track.coverId.startsWith('http')
transform-style: preserve-3d;
}
.flip-card[data-flipped=false] .flip-inner {
.flipped .flip-inner {
transform: rotateY(180deg);
}

View File

@@ -1,7 +1,14 @@
<template>
<audio ref="audioRef" class="fixed z-50 bottom-0 left-1/2 -translate-x-1/2 w-1/2"
:src="playerStore.currentTrack ? playerStore.getCompilationUrlFromTrack(playerStore.currentTrack) : ''" controls
@timeupdate="updatePosition" @ended="onEnded" />
<div class="fixed left-0 bottom-0 opacity-1 z-50 w-full bg-white transition-all"
:class="{ '-bottom-20 opacity-0': !playerStore.currentTrack }">
<!-- <p class="hidden">
{{ Math.round(currentTime) }}
{{ Math.round(currentProgression) }}%
</p> -->
<audio ref="audioRef" class="w-full"
:src="playerStore.currentTrack ? playerStore.getCompilationUrlFromTrack(playerStore.currentTrack) : ''"
controls />
</div>
</template>
<script setup lang="ts">
@@ -10,39 +17,36 @@ import { usePlayerStore } from '~/store/player'
const playerStore = usePlayerStore()
const audioRef = ref<HTMLAudioElement | null>(null)
const currentTime = ref(0)
const lastValidProgression = ref(0)
onMounted(() => {
if (audioRef.value) playerStore.audio = audioRef.value
const currentProgression = computed(() => {
if (!audioRef.value) return 0
const progression = (currentTime.value / audioRef.value.duration) * 100
if (!isNaN(progression)) {
lastValidProgression.value = progression
}
return lastValidProgression.value
})
// Mettre à jour la position
function updatePosition() {
if (audioRef.value) playerStore.position = audioRef.value.currentTime
}
function onEnded() {
playerStore.isPlaying = false
}
// Si la track change, mettre à jour le src et le start
watch(
() => playerStore.currentTrack,
(newTrack) => {
if (newTrack && audioRef.value) {
audioRef.value.src = newTrack.url
audioRef.value.currentTime = newTrack.start || 0
if (playerStore.isPlaying) audioRef.value.play()
}
function updateTime() {
if (audioRef.value) {
currentTime.value = audioRef.value.currentTime
}
)
}
onMounted(() => {
if (audioRef.value) {
playerStore.audio = audioRef.value
audioRef.value.addEventListener("timeupdate", updateTime)
}
})
onUnmounted(() => {
if (audioRef.value) {
audioRef.value.removeEventListener("timeupdate", updateTime)
}
})
</script>
<style scoped>
audio {
transition: all 1s;
}
audio[src=""] {
@apply -bottom-1.5 opacity-0
}
</style>

View File

@@ -9,7 +9,6 @@
<script setup lang="ts">
import { useDataStore } from '~/store/data'
import type { BoxState } from '~~/types/types'
import { useRouter } from 'vue-router'
const dataStore = useDataStore()
const boxStates = ref<Record<string, BoxState>>({})
@@ -30,7 +29,6 @@ function closeCompilation(e: KeyboardEvent) {
}
}
onMounted(async () => {
const dataStore = await useDataStore()
await dataStore.loadData()

View File

@@ -1,7 +1,6 @@
<template>
<div class="mt-8 p-8 w-full flex flex-wrap justify-around">
<MoleculeCard v-for="track in dataStore.getTracksByCompilationId(compilation.id)" :key="track.id" :track="track"
:isFlipped="true" />
<MoleculeCard v-for="track in dataStore.getTracksByCompilationId(compilation.id)" :key="track.id" :track="track" />
</div>
</template>
@@ -14,4 +13,4 @@ const props = defineProps<{
}>()
const dataStore = useDataStore()
</script>
</script>