bucket + card sharer
This commit is contained in:
@@ -1,100 +1,38 @@
|
||||
<template>
|
||||
<div class="platine" :class="{ 'drag-over': isDragOver }" @dragenter.prevent="onDragEnter"
|
||||
<div class="platine pointer-events-none" :class="{ 'drag-over': isDragOver }" @dragenter.prevent="onDragEnter"
|
||||
@dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
|
||||
<card v-if="currentTrack" :track="currentTrack" />
|
||||
<div class="disc fixed" ref="discRef" :style="'background-image: url(/card-dock.svg)'" id="disc">
|
||||
<div class="disc pointer-events-auto fixed" ref="discRef" :style="'background-image: url(/card-dock.svg)'"
|
||||
id="disc">
|
||||
<div
|
||||
class="bobine bg-slate-800 bg-opacity-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full"
|
||||
:style="{ height: progressPercentage + '%', width: progressPercentage + '%' }">
|
||||
</div>
|
||||
class="bobine bg-slate-900 bg-opacity-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full"
|
||||
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
|
||||
|
||||
<div class="disc-label rounded-full bg-cover bg-center">
|
||||
<img src="/favicon.svg" class="size-1/3">
|
||||
<div v-if="isLoadingTrack" class="loading-indicator">
|
||||
<div v-if="platineStore.isLoadingTrack" class="loading-indicator">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
||||
<div class="w-full h-1/5 flex justify-center items-center text-8xl text-white absolute pointer-events-none">
|
||||
{{ platineStore.currentTrack?.title }}
|
||||
<br>
|
||||
{{ platineStore.currentTrack?.artist.name }}
|
||||
</div>
|
||||
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
||||
</div>
|
||||
</div>
|
||||
<button class="rewind absolute left-0 bottom-0" @click="toggleMute">mute</button>
|
||||
<button class="power absolute right-0 bottom-0" :class="{ 'is-active': isPlaying }"
|
||||
@click="togglePlay">power</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import Disc from '@/platine-tools/disc'
|
||||
import Sampler from '@/platine-tools/sampler'
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { usePlatineStore } from '~/store/platine'
|
||||
import type { Track } from '~~/types/types'
|
||||
|
||||
const props = withDefaults(defineProps<{ track?: Track }>(), {})
|
||||
const props = defineProps<{ track?: Track }>()
|
||||
const platineStore = usePlatineStore()
|
||||
const discRef = ref<HTMLElement>()
|
||||
const currentTurns = ref(0)
|
||||
const totalTurns = ref(0)
|
||||
const progressPercentage = ref(0)
|
||||
const drag = ref(false)
|
||||
const isDragOver = ref(false)
|
||||
const isFirstDrag = ref(true)
|
||||
const isLoadingTrack = ref(false)
|
||||
const isPlaying = ref(false)
|
||||
const coverUrl = computed(() => currentTrack.value?.coverId || '/card-dock.svg')
|
||||
|
||||
let disc: Disc | null = null
|
||||
let sampler: Sampler | null = null
|
||||
|
||||
const currentTrack = ref()
|
||||
|
||||
const updateTurns = (disc: Disc) => {
|
||||
currentTurns.value = disc.secondsPlayed * 0.75 // 0.75 tours par seconde (RPS)
|
||||
totalTurns.value = (disc as any)._duration * 0.75 // Accès à la propriété _duration privée
|
||||
progressPercentage.value = Math.min(100, (disc.secondsPlayed / (disc as any)._duration) * 100)
|
||||
};
|
||||
|
||||
const startPlayback = () => {
|
||||
if (!disc || !sampler || !currentTrack.value || isPlaying.value) return
|
||||
|
||||
isPlaying.value = true
|
||||
sampler.play(disc.secondsPlayed)
|
||||
disc.powerOn()
|
||||
}
|
||||
|
||||
const togglePlay = () => {
|
||||
if (!disc || !sampler || !currentTrack.value) return
|
||||
|
||||
isPlaying.value = !isPlaying.value
|
||||
|
||||
if (isPlaying.value) {
|
||||
startPlayback()
|
||||
} else {
|
||||
disc.powerOff()
|
||||
}
|
||||
};
|
||||
|
||||
const toggleMute = () => {
|
||||
if (!sampler) return
|
||||
|
||||
sampler.mute()
|
||||
}
|
||||
|
||||
const loadTrack = async (track: Track) => {
|
||||
if (!sampler || !track) return
|
||||
|
||||
currentTrack.value = track
|
||||
isLoadingTrack.value = true
|
||||
try {
|
||||
await sampler.loadTrack(currentTrack.value.url)
|
||||
if (disc) {
|
||||
disc.setDuration(sampler.duration)
|
||||
updateTurns(disc)
|
||||
sampler.play(0)
|
||||
disc.secondsPlayed = 0
|
||||
disc.powerOn()
|
||||
}
|
||||
} finally {
|
||||
isLoadingTrack.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion du drag and drop
|
||||
const onDragEnter = (e: DragEvent) => {
|
||||
@@ -111,80 +49,41 @@ const onDragLeave = () => {
|
||||
isDragOver.value = false
|
||||
}
|
||||
|
||||
const onDragStart = () => {
|
||||
drag.value = true
|
||||
}
|
||||
|
||||
const onDragEnd = () => {
|
||||
drag.value = false
|
||||
isDragOver.value = false
|
||||
}
|
||||
|
||||
const onDrop = (e: DragEvent) => {
|
||||
isDragOver.value = false
|
||||
const cardData = e.dataTransfer?.getData('application/json')
|
||||
|
||||
if (cardData) {
|
||||
try {
|
||||
const newTrack = JSON.parse(cardData)
|
||||
if (newTrack && newTrack.url) {
|
||||
loadTrack(newTrack)
|
||||
disc?.powerOn()
|
||||
platineStore.loadTrack(newTrack)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du traitement de la carte déposée', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
disc = new Disc(discRef.value!)
|
||||
sampler = new Sampler()
|
||||
|
||||
disc.callbacks.onStop = () => {
|
||||
sampler?.pause()
|
||||
}
|
||||
|
||||
disc.callbacks.onDragStart = () => {
|
||||
if (isFirstDrag.value) {
|
||||
isFirstDrag.value = false
|
||||
isPlaying.value = true
|
||||
if (sampler && disc) {
|
||||
sampler.play(disc.secondsPlayed)
|
||||
disc.powerOn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disc.callbacks.onDragEnded = () => {
|
||||
if (!isPlaying.value) {
|
||||
return
|
||||
}
|
||||
sampler?.play(disc?.secondsPlayed)
|
||||
}
|
||||
|
||||
disc.callbacks.onLoop = ({ playbackSpeed, isReversed, secondsPlayed }) => {
|
||||
sampler?.updateSpeed(playbackSpeed, isReversed, secondsPlayed);
|
||||
if (disc) {
|
||||
updateTurns(disc);
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
watch(() => props.track, (propTrack) => {
|
||||
if (propTrack) {
|
||||
loadTrack(propTrack)
|
||||
// Initialisation du lecteur
|
||||
onMounted(() => {
|
||||
if (discRef.value) {
|
||||
platineStore.initPlatine(discRef.value)
|
||||
}
|
||||
})
|
||||
|
||||
// Nettoyage
|
||||
onUnmounted(() => {
|
||||
if (disc) {
|
||||
disc.stop();
|
||||
disc.powerOff();
|
||||
platineStore.cleanup()
|
||||
})
|
||||
|
||||
// Surveillance des changements de piste
|
||||
watch(() => props.track, (newTrack) => {
|
||||
if (newTrack) {
|
||||
platineStore.loadTrack(newTrack)
|
||||
}
|
||||
if (sampler) {
|
||||
sampler.pause();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -198,6 +97,7 @@ onUnmounted(() => {
|
||||
.card {
|
||||
position: absolute !important;
|
||||
z-index: 99;
|
||||
top: -20%;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
transform: translate(-50%, 50%);
|
||||
@@ -307,4 +207,17 @@ onUnmounted(() => {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.bobine {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user