dupont release
All checks were successful
Deploy App / build (push) Successful in 2m1s
Deploy App / deploy (push) Successful in 20s

This commit is contained in:
valere
2026-02-13 17:20:00 +01:00
parent 399519d1d4
commit 543b513e08
8 changed files with 130 additions and 45 deletions

View File

@@ -13,10 +13,10 @@
<Rank :card="props.card" /> <Rank :card="props.card" />
<!-- Cover --> <!-- Cover -->
<figure class="flex-1 flex justify-center items-center overflow-hidden cursor-pointer"> <figure class="cover">
<PlayButton /> <PlayButton />
<img :src="props.card.url_image" alt="Pochette de l'album" :loading="props.imageLoadingType" <img :src="props.card.url_image" alt="cover-image de l'album" :loading="props.imageLoadingType"
@load="$emit('image-loaded', $event)" class="pochette w-full h-full object-cover object-center" /> @load="$emit('image-loaded', $event)" class="cover-image w-full h-full object-cover object-center" />
</figure> </figure>
<!-- Body --> <!-- Body -->
@@ -92,11 +92,7 @@ const isTrackLoaded = ref(false)
/* Flip effect */ /* Flip effect */
.card { .card {
perspective: 1000px; perspective: 1000px;
@apply transition-all scale-100 w-56 h-80 min-w-56 min-h-80; @apply transition-all scale-100 size-full;
.pochette {
border-radius: 0.75rem;
}
.flip-inner { .flip-inner {
position: relative; position: relative;
@@ -121,6 +117,14 @@ const isTrackLoaded = ref(false)
border-radius: 1rem; border-radius: 1rem;
transform: rotateY(0deg); transform: rotateY(0deg);
transition: box-shadow 0.6s; transition: box-shadow 0.6s;
.cover {
@apply flex-1 flex justify-center items-center overflow-hidden cursor-pointer;
.cover-image {
border-radius: 0.75rem;
}
}
} }
.face-down { .face-down {
@@ -200,7 +204,7 @@ const isTrackLoaded = ref(false)
} }
} }
.pochette:active, .cover-image:active,
.face-down:active { .face-down:active {
.play-button { .play-button {
@apply scale-90; @apply scale-90;

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="platine" :class="{ 'loading': isLoadingTrack, 'mounted': isMounted, 'playing': isPlaying }" ref="platine"> <div class="platine" :class="{ 'loading': isLoadingTrack, 'mounted': isMounted, 'playing': isPlaying }" ref="platine">
<div v-if="true" class="debug"> <div v-if="false" class="debug">
<button @click="Reverse"> <button @click="Reverse">
<b v-if="isReversed">reversed</b> <b v-if="isReversed">reversed</b>
<b v-else>normal</b> <b v-else>normal</b>
@@ -20,8 +20,9 @@
width: progressPercentage + '%' width: progressPercentage + '%'
}"></div> }"></div>
<button class="power-button" @click="Power" @touchstart="Power" :disabled="isLoadingTrack"> <button class="power-button" @click="Power" @touchstart="Power" :disabled="isLoadingTrack">
<img class="macaron" src="/favicon.svg"> <img class="Macaron" src="/favicon.svg" v-if="!isEndOfTrack">
<div class="spinner" v-if="isLoadingTrack" /> <div class="spinner" v-if="isLoadingTrack" />
<img class="Macaron" @click="Rewind" src="/rewind.svg" v-if="isEndOfTrack" />
</button> </button>
<div class="turn-point" v-if="!isLoadingTrack"> <div class="turn-point" v-if="!isLoadingTrack">
</div> </div>
@@ -42,6 +43,8 @@ const autoplay = props.autoplay ?? false
// State // State
const isLoadingTrack = ref(false) const isLoadingTrack = ref(false)
const isFirstPlay = ref(true) const isFirstPlay = ref(true)
const isEndOfTrack = ref(false)
const progressPercentage = ref(0) const progressPercentage = ref(0)
const currentTurns = ref(0) const currentTurns = ref(0)
const totalTurns = ref(0)// Refs pour les instances const totalTurns = ref(0)// Refs pour les instances
@@ -111,6 +114,7 @@ const updateTurns = () => {
currentTurns.value = newTurns currentTurns.value = newTurns
totalTurns.value = newTotalTurns totalTurns.value = newTotalTurns
progressPercentage.value = newProgressPercentage progressPercentage.value = newProgressPercentage
isEndOfTrack.value = disc.value.secondsPlayed + 0.1 >= (disc.value as any)._duration
} }
} }
@@ -171,6 +175,9 @@ const cleanup = () => {
} }
const Power = (e: MouseEvent) => { const Power = (e: MouseEvent) => {
if (isEndOfTrack.value) {
Rewind()
}
togglePlay() togglePlay()
} }
@@ -221,6 +228,7 @@ const Rewind = async () => {
} }
requestAnimationFrame(slowDown) requestAnimationFrame(slowDown)
play()
} }
const performRewind = () => { const performRewind = () => {
@@ -324,6 +332,7 @@ watch(() => props.card, (newCard) => {
.platine { .platine {
overflow: hidden; overflow: hidden;
position: relative; position: relative;
display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -365,12 +374,12 @@ watch(() => props.card, (newCard) => {
transform-origin: center; transform-origin: center;
width: 33%; width: 33%;
height: 33%; height: 33%;
border-radius: 999px; border-radius: 100%;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
.macaron { .Macaron {
position: absolute; position: absolute;
filter: grayscale(1); filter: grayscale(1);
transition: transform 0.1s, filter 0.8s; transition: transform 0.1s, filter 0.8s;
@@ -382,13 +391,13 @@ watch(() => props.card, (newCard) => {
} }
&:active { &:active {
.macaron { .Macaron {
transform: scale(0.8); transform: scale(0.8);
} }
} }
} }
&.playing .power-button .macaron { &.playing .power-button .Macaron {
filter: grayscale(0); filter: grayscale(0);
} }
@@ -426,7 +435,7 @@ watch(() => props.card, (newCard) => {
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
@apply relative bg-black bg-opacity-30 backdrop-blur-sm rounded-full; @apply relative bg-black bg-opacity-60 backdrop-blur-sm rounded-full;
} }
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<section class="playing-card" :class="{ 'playing-card--platine-open': platineOpen }"> <section class="playing-card" :class="{ 'platine-open': platineOpen }">
<Card :card="props.card!" :is-face-up="props.isFaceUp" @click="clickOnCard" :role="platineOpen ? 'img' : 'button'" <Card :card="props.card!" :is-face-up="props.isFaceUp" @click="clickOnCard" :role="platineOpen ? 'img' : 'button'"
showPlayButtonFaceUp /> showPlayButtonFaceUp />
<Platine v-if="platineOpen && card" :card="card" autoplay /> <Platine v-if="platineOpen && card" :card="card" autoplay />
@@ -15,7 +15,9 @@ const props = defineProps<{ card: Card, isFaceUp: boolean }>()
const platineOpen = ref(false) const platineOpen = ref(false)
const clickOnCard = () => { const clickOnCard = () => {
platineOpen.value = !platineOpen.value setTimeout(() => {
platineOpen.value = !platineOpen.value
}, 600);
} }
</script> </script>
@@ -23,16 +25,23 @@ const clickOnCard = () => {
$open-speed: 0.4s; $open-speed: 0.4s;
.playing-card { .playing-card {
display: flex; @apply size-card;
justify-content: center; position: relative;
align-items: center;
.card,
.platine {
height: 100%;
width: 100%;
transition: width $open-speed, height $open-speed, min-width $open-speed,
min-height $open-speed, transform 0.1s;
}
:deep(.card) { :deep(.card) {
transition: width $open-speed, height $open-speed, transform 0.1s; // transition: width $open-speed, height $open-speed, transform 0.1s;
position: absolute; position: absolute;
.face-up, .face-up,
.pochette { .cover {
transition: border-radius $open-speed, box-shadow $open-speed; transition: border-radius $open-speed, box-shadow $open-speed;
&:active { &:active {
@@ -43,7 +52,13 @@ $open-speed: 0.4s;
} }
} }
&--platine-open { &.platine-open {
$open-size: 20rem;
width: $open-size;
height: $open-size;
min-height: $open-size;
min-width: $open-size;
.card { .card {
pointer-events: none; pointer-events: none;
@@ -52,19 +67,13 @@ $open-speed: 0.4s;
} }
} }
.card,
.platine {
width: 100vmin;
height: 100vmin;
}
&:deep(.card) { &:deep(.card) {
.face-up { .face-up {
border-radius: 999px; border-radius: 100%;
@apply shadow-xl; @apply shadow-xl;
.pochette { .cover {
border-radius: 999px; border-radius: 100%;
} }
.rank, .rank,

View File

@@ -1,6 +1,6 @@
<template> <template>
<button ref="buttonRef"> <button ref="buttonRef">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier"> <g id="SVGRepo_iconCarrier">
@@ -34,6 +34,6 @@ onUnmounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
button { button {
@apply fixed bottom-4 md:top-4 left-4 size-20; @apply bottom-4 md:top-4 left-4 size-20 fill-slate-600 bg-slate-500;
} }
</style> </style>

View File

@@ -1,15 +1,14 @@
<template> <template>
<section class="deck"> <section class="deck">
<div v-for="(card, index) in cards" :key="index"> <PlayingCard v-for="(card, index) in cards" :key="index" :card="card" :isFaceUp="isFaceUp[index] || false"
<PlayingCard :card="card" :isFaceUp="isFaceUp[index] || false" /> @click="isFaceUp[index] = true" />
</div>
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { Card } from '~~/types/types' import type { Card } from '~~/types/types'
const nbCards = 1 // Nombre de cartes à afficher const nbCards = Math.floor(Math.random() * 14) + 1 // Nombre de cartes à afficher (aléatoire entre 1 et 15)
const cards = ref<Card[]>([]) const cards = ref<Card[]>([])
const isFaceUp = ref<boolean[]>([]) const isFaceUp = ref<boolean[]>([])
@@ -31,7 +30,7 @@ const loadCards = async () => {
const flipCards = () => { const flipCards = () => {
isFaceUp.value.forEach((_, index) => { isFaceUp.value.forEach((_, index) => {
setTimeout(() => { setTimeout(() => {
isFaceUp.value[index] = true isFaceUp.value[index] = false
}, 400 * (index + 1)) }, 400 * (index + 1))
}) })
} }
@@ -50,9 +49,14 @@ useHead({
<style lang="scss" scoped> <style lang="scss" scoped>
.deck { .deck {
@apply screen-centered h-screen; display: flex;
display: grid; justify-content: center;
grid-template-columns: 1fr 1fr 1fr; align-items: center;
grid-template-rows: 1fr 1fr 1fr; flex-wrap: wrap;
min-height: 100vh;
.playing-card {
margin: 1rem;
}
} }
</style> </style>

4
public/old.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg fill="none" height="36" viewBox="0 0 36 36" width="36">
<path d="M11.29 2.92C14.85 1.33 18.87 1.06 22.61 2.15L22.96 2.26C26.56 3.40 29.67 5.74 31.75 8.89L31.95 9.19C33.90 12.28 34.77 15.93 34.42 19.56L34.38 19.93C34.04 22.79 32.96 25.51 31.25 27.83C29.53 30.14 27.23 31.97 24.59 33.12C21.95 34.27 19.05 34.71 16.18 34.40C13.32 34.08 10.59 33.02 8.26 31.32L7.97 31.10C4.87 28.73 2.71 25.33 1.88 21.52L3.34 21.20L4.81 20.88C5.49 24.00 7.25 26.77 9.79 28.72L10.27 29.07C12.19 30.40 14.41 31.22 16.74 31.44C19.06 31.65 21.40 31.27 23.53 30.31C25.66 29.35 27.50 27.86 28.88 25.98C30.26 24.10 31.13 21.89 31.40 19.58L31.46 18.98C31.68 16.00 30.90 13.03 29.25 10.54C27.60 8.05 25.17 6.18 22.34 5.22L21.77 5.04C19.02 4.23 16.08 4.33 13.38 5.31C10.68 6.29 8.37 8.11 6.77 10.5H10.5L10.65 10.50C11.03 10.54 11.38 10.73 11.63 11.02C11.88 11.31 12.01 11.69 11.99 12.07C11.97 12.46 11.81 12.82 11.53 13.08C11.25 13.35 10.88 13.49 10.5 13.5H1.5V4.5L1.50 4.34C1.54 3.97 1.71 3.63 1.99 3.38C2.27 3.13 2.62 3.00 3 3.00C3.37 3.00 3.72 3.13 4.00 3.38C4.28 3.63 4.45 3.97 4.49 4.34L4.5 4.5V8.51C6.21 6.07 8.56 4.13 11.29 2.92ZM24 18L15 12.75V23.25L24 18ZM3.02 19.73C2.63 19.82 2.29 20.05 2.08 20.39C1.86 20.72 1.79 21.13 1.88 21.52L4.81 20.88C4.77 20.69 4.69 20.50 4.57 20.34C4.46 20.18 4.32 20.04 4.15 19.94C3.99 19.83 3.80 19.76 3.61 19.72C3.41 19.69 3.21 19.69 3.02 19.73Z" fill="white">
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

41
public/rewind.svg Normal file
View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="none"
height="50"
viewBox="0 0 50 50"
width="50"
version="1.1"
id="svg1"
sodipodi:docname="rewind.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="8.9665068"
inkscape:cx="15.334846"
inkscape:cy="20.018944"
inkscape:window-width="1920"
inkscape:window-height="1132"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="M 15.44514,3.5259965 C 20.516099,1.2611577 26.242296,0.87656242 31.569652,2.4291878 l 0.49855,0.1566869 c 5.127937,1.6238467 9.557904,4.9570057 12.520712,9.4439503 l 0.284886,0.427328 c 2.777632,4.401479 4.016884,9.600638 3.518334,14.771307 l -0.05698,0.527038 c -0.484305,4.073861 -2.022686,7.948303 -4.458456,11.252973 -2.450014,3.290426 -5.726196,5.897127 -9.486683,7.535218 -3.760488,1.638091 -7.891326,2.264839 -11.979431,1.823267 -4.073861,-0.455817 -7.962546,-1.965709 -11.281461,-4.387235 L 10.716042,43.666347 C 6.3003187,40.290455 3.2235566,35.447404 2.0412822,30.020337 l 2.0796633,-0.455816 2.0939076,-0.455817 c 0.9686103,4.444212 3.4756017,8.389875 7.0936459,11.167507 l 0.683725,0.498549 c 2.7349,1.894488 5.897128,3.062518 9.216042,3.375892 3.304671,0.29913 6.63783,-0.242152 9.671859,-1.609602 3.034029,-1.36745 5.654975,-3.489846 7.620684,-6.167769 1.965709,-2.677922 3.204961,-5.825906 3.589556,-9.116332 l 0.08546,-0.854656 C 44.489204,22.157501 43.378151,17.926953 41.027847,14.38013 38.677543,10.833307 35.216185,8.1696282 31.185057,6.8021784 L 30.373134,6.5457816 C 26.45596,5.3919958 22.268144,5.5344384 18.422192,6.9303768 14.576239,8.3263152 11.285813,10.918772 9.0067299,14.323153 h 5.3131121 0.213664 c 0.541283,0.05698 1.039832,0.327618 1.395939,0.740702 0.356106,0.413083 0.541282,0.954366 0.512793,1.495648 -0.02849,0.555526 -0.256396,1.06832 -0.655236,1.438671 -0.39884,0.384595 -0.925877,0.584015 -1.46716,0.598259 H 1.5 V 5.776591 5.5486827 C 1.5569771,5.0216448 1.7991297,4.5373396 2.1979692,4.1812329 2.5968087,3.8251261 3.0953581,3.6399506 3.6366404,3.6399506 c 0.5270379,0 1.0255874,0.1851755 1.4244269,0.5412823 0.3988395,0.3561067 0.6409921,0.8404119 0.6979692,1.3674498 l 0.014244,0.2279083 v 5.711952 C 8.2090508,8.0129413 11.556454,5.2495531 15.44514,3.5259965 Z M 3.6651289,27.470613 C 3.1096024,27.598811 2.6252973,27.92643 2.3261676,28.410735 2.0127937,28.880796 1.9130838,29.464811 2.0412822,30.020337 L 6.2148531,29.108704 C 6.157876,28.838063 6.0439219,28.567422 5.8729906,28.339513 5.7163037,28.111605 5.5168839,27.912185 5.2747313,27.769743 5.046823,27.613056 4.7761819,27.513346 4.5055408,27.456369 4.2206554,27.413636 3.93577,27.413636 3.6651289,27.470613 Z"
fill="#ffffff"
id="path1"
sodipodi:nodetypes="ccccccccscccccccccssccsccscccsccccccssscccccccccccc"
style="stroke-width:1.42443" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -39,8 +39,22 @@ module.exports = {
display: 'grid', display: 'grid',
'place-items': 'center' 'place-items': 'center'
}, },
'full-screen': {
height: '100dvh',
width: '100dvw'
},
'.size-card': {
width: '14rem',
height: '20rem',
'min-width': '14rem',
'min-height': '20rem'
},
'.size-100vmin': {
width: '100vmin',
height: '100vmin'
},
'.debug': { '.debug': {
position: 'fixed', position: 'absolute',
'z-index': '1000', 'z-index': '1000',
bottom: '16px', bottom: '16px',
right: '25%', right: '25%',