eS v1
This commit is contained in:
@@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<article class="box box-scene z-10" ref="scene">
|
||||
<div class="box-object" ref="box">
|
||||
<div ref="domBox" class="box-object" :class="{ 'is-draggable': isDraggable }">
|
||||
<div class="face front relative" ref="frontFace">
|
||||
<img v-if="compilation.duration" class="cover absolute" :src="`/${compilation.id}/cover.jpg`" alt="">
|
||||
<div class="size-full flex justify-center items-center text-7xl" v-else>
|
||||
{{ compilation.description }}
|
||||
</div>
|
||||
<img v-if="box.duration" class="cover absolute" :src="`/${box.id}/cover.jpg`" alt="">
|
||||
<div class="size-full flex-col justify-center items-center text-7xl" v-html="box.description" v-else />
|
||||
</div>
|
||||
<div class="face back flex flex-col flex-wrap items-start p-4 overflow-hidden" ref="backFace">
|
||||
<li class="list-none text-xxs w-1/2 flex flex-row"
|
||||
v-for="track in dataStore.getTracksByCompilationId(compilation.id).slice(0, -1)" :key="track.id"
|
||||
:track="track">
|
||||
v-for="track in dataStore.getTracksByboxId(box.id).slice(0, -1)" :key="track.id" :track="track">
|
||||
<span class="" v-if="isNotManifesto">
|
||||
{{ track.order }}.
|
||||
</span>
|
||||
@@ -24,9 +21,9 @@
|
||||
<div class="face right" ref="rightFace" />
|
||||
<div class="face left" ref="leftFace" />
|
||||
<div class="face top" ref="topFace">
|
||||
<template v-if="compilation.duration !== 0">
|
||||
<template v-if="box.duration !== 0">
|
||||
<img class="logo h-full p-1" src="/logo.svg" alt="">
|
||||
<img class="absolute block h-1/2" style="left:5%;" :src="`/${compilation.id}/title.svg`" alt="">
|
||||
<img class="absolute block h-1/2" style="left:5%;" :src="`/${box.id}/title.svg`" alt="">
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="absolute block h-1/2 right-6">
|
||||
@@ -34,7 +31,7 @@
|
||||
</span>
|
||||
<img class="logo h-full p-1" src="/favicon.svg" alt="">
|
||||
<span class="absolute block h-1/2" style="left:5%;">
|
||||
{{ compilation.name }}
|
||||
{{ box.name }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
@@ -45,24 +42,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import type { Compilation, BoxState } from '~~/types/types'
|
||||
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
|
||||
import type { Box, BoxState } from '~~/types/types'
|
||||
import { useDataStore } from '~/store/data'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
compilation: Compilation
|
||||
BoxState?: BoxState
|
||||
}>(),
|
||||
{ BoxState: 'list' }
|
||||
)
|
||||
const props = defineProps<{
|
||||
box: Box
|
||||
}>();
|
||||
|
||||
const { $isMobile } = useNuxtApp()
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const isDraggable = computed(() => !['list', 'hidden'].includes(BoxState.value()))
|
||||
const isNotManifesto = computed(() => !props.compilation.id.startsWith('ES00'))
|
||||
const isDraggable = computed(() => !['box-list', 'box-hidden'].includes(props.box.state))
|
||||
const isNotManifesto = computed(() => !props.box.id.startsWith('ES00'))
|
||||
|
||||
// --- Réfs ---
|
||||
const scene = ref<HTMLElement>()
|
||||
const box = ref<HTMLElement>()
|
||||
const domBox = ref<HTMLElement>()
|
||||
const frontFace = ref<HTMLElement>()
|
||||
const backFace = ref<HTMLElement>()
|
||||
const rightFace = ref<HTMLElement>()
|
||||
@@ -81,36 +77,36 @@ let lastPointer = { x: 0, y: 0, time: 0 }
|
||||
let velocity = { x: 0, y: 0 }
|
||||
let raf: number | null = null
|
||||
|
||||
const sensitivity = 0.3
|
||||
const sensitivity = $isMobile ? 0.5 : 0.15
|
||||
const friction = 0.95
|
||||
const minVelocity = 0.02
|
||||
const enableInertia = true
|
||||
|
||||
// --- Transformations ---
|
||||
function applyTransform(duration = 0.5) {
|
||||
if (!box.value) return
|
||||
if (!domBox.value) return
|
||||
rotateX.value = Math.round(rotateX.value)
|
||||
rotateY.value = Math.round(rotateY.value)
|
||||
rotateZ.value = Math.round(rotateZ.value)
|
||||
|
||||
box.value.style.transition = `transform ${duration}s ease`
|
||||
box.value.style.transform = `rotateX(${rotateX.value}deg) rotateY(${rotateY.value}deg) rotateZ(${rotateZ.value}deg)`
|
||||
domBox.value.style.transition = `transform ${duration}s ease`
|
||||
domBox.value.style.transform = `rotateX(${rotateX.value}deg) rotateY(${rotateY.value}deg) rotateZ(${rotateZ.value}deg)`
|
||||
}
|
||||
|
||||
// --- Gestion BoxState ---
|
||||
function applyBoxState() {
|
||||
switch (props.BoxState) {
|
||||
case 'list':
|
||||
switch (props.box.state) {
|
||||
case 'box-list':
|
||||
rotateX.value = 76
|
||||
rotateY.value = 0
|
||||
rotateZ.value = 150
|
||||
break
|
||||
case 'selected':
|
||||
case 'box-selected':
|
||||
rotateX.value = -20
|
||||
rotateY.value = 20
|
||||
rotateZ.value = 0
|
||||
break
|
||||
case 'hide':
|
||||
case 'box-hidden':
|
||||
rotateX.value = 76
|
||||
rotateY.value = 0
|
||||
rotateZ.value = 150
|
||||
@@ -123,12 +119,12 @@ function applyBoxState() {
|
||||
function applyColor() {
|
||||
if (!frontFace.value || !backFace.value || !leftFace.value || !topFace.value || !bottomFace.value) return
|
||||
|
||||
frontFace.value.style.background = props.compilation.color2
|
||||
backFace.value.style.background = `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`
|
||||
leftFace.value.style.background = `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`
|
||||
rightFace.value.style.background = `linear-gradient(to top, ${props.compilation.color1}, ${props.compilation.color2})`
|
||||
topFace.value.style.background = `linear-gradient(to top, ${props.compilation.color2}, ${props.compilation.color2})`
|
||||
bottomFace.value.style.background = props.compilation.color1
|
||||
frontFace.value.style.background = props.box.color2
|
||||
backFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
|
||||
leftFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
|
||||
rightFace.value.style.background = `linear-gradient(to top, ${props.box.color1}, ${props.box.color2})`
|
||||
topFace.value.style.background = `linear-gradient(to top, ${props.box.color2}, ${props.box.color2})`
|
||||
bottomFace.value.style.background = props.box.color1
|
||||
}
|
||||
|
||||
// --- Inertie ---
|
||||
@@ -157,7 +153,7 @@ let listenersAttached = false
|
||||
const down = (ev: PointerEvent) => {
|
||||
ev.preventDefault()
|
||||
dragging = true
|
||||
box.value?.setPointerCapture(ev.pointerId)
|
||||
domBox.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 }
|
||||
@@ -185,36 +181,36 @@ const move = (ev: PointerEvent) => {
|
||||
const end = (ev: PointerEvent) => {
|
||||
if (!dragging) return
|
||||
dragging = false
|
||||
try { box.value?.releasePointerCapture(ev.pointerId) } catch { }
|
||||
try { domBox.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)
|
||||
if (!domBox.value || listenersAttached) return
|
||||
domBox.value.addEventListener('pointerdown', down)
|
||||
domBox.value.addEventListener('pointermove', move)
|
||||
domBox.value.addEventListener('pointerup', end)
|
||||
domBox.value.addEventListener('pointercancel', end)
|
||||
domBox.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)
|
||||
if (!domBox.value || !listenersAttached) return
|
||||
domBox.value.removeEventListener('pointerdown', down)
|
||||
domBox.value.removeEventListener('pointermove', move)
|
||||
domBox.value.removeEventListener('pointerup', end)
|
||||
domBox.value.removeEventListener('pointercancel', end)
|
||||
domBox.value.removeEventListener('pointerleave', end)
|
||||
listenersAttached = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
applyColor()
|
||||
applyBoxState()
|
||||
if (isDraggable) addListeners()
|
||||
if (isDraggable.value) addListeners()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -223,9 +219,9 @@ onBeforeUnmount(() => {
|
||||
})
|
||||
|
||||
// --- Watchers ---
|
||||
watch(() => props.BoxState, () => applyBoxState())
|
||||
watch(() => props.compilation, () => applyColor(), { deep: true })
|
||||
watch(() => isDraggable, (enabled) => enabled ? addListeners() : removeListeners())
|
||||
watch(() => props.box.state, () => applyBoxState())
|
||||
watch(() => props.box, () => applyColor(), { deep: true })
|
||||
watch(isDraggable, (enabled) => (enabled ? addListeners() : removeListeners()))
|
||||
|
||||
</script>
|
||||
|
||||
@@ -235,23 +231,29 @@ watch(() => isDraggable, (enabled) => enabled ? addListeners() : removeListeners
|
||||
--height: calc(var(--size) * (100 / 3));
|
||||
--width: calc(var(--size) * 50);
|
||||
--depth: calc(var(--size) * 10);
|
||||
transition: all .5s;
|
||||
transition: height .5s ease, opacity .5s ease;
|
||||
|
||||
&.hide {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
&.box-list {
|
||||
height: calc(var(--size) * 20);
|
||||
@apply hover:scale-105;
|
||||
transition: all .5s ease;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
&.list {
|
||||
@apply hover:scale-105;
|
||||
&.box-selected {
|
||||
height: calc(var(--size) * 34);
|
||||
}
|
||||
|
||||
&-scene {
|
||||
height: calc(var(--size) * 20);
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
&.box-hidden {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&-object {
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
@@ -260,11 +262,11 @@ watch(() => isDraggable, (enabled) => enabled ? addListeners() : removeListeners
|
||||
margin: auto;
|
||||
user-select: none;
|
||||
|
||||
.list & {
|
||||
.box-list & {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected & {
|
||||
.box-selected & {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
@@ -339,5 +341,31 @@ watch(() => isDraggable, (enabled) => enabled ? addListeners() : removeListeners
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Deck fade in/out purely in CSS */
|
||||
.box-page {
|
||||
opacity: 0;
|
||||
transition: opacity .25s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.box-selected .box-page {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* for tabindex */
|
||||
&:focus-visible {
|
||||
outline: 0;
|
||||
@apply scale-105 outline-none;
|
||||
|
||||
.face {
|
||||
border: 4px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.indice) {
|
||||
@apply text-xl p-2 relative bg-black/50 rounded-full backdrop-blur-md;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user