<script setup lang="ts">
import { AnimateView, spring, startTransition, wrap } from 'motion-v'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import { ref } from 'vue'
const images = Array.from(
{ length: 6 },
(_, i) => `/photos/prague/image-${String(i + 1).padStart(2, '0')}.jpg`,
)
// Preload so slides don't pop in mid-transition
images.forEach((src) => {
const img = new Image()
img.src = src
})
const index = ref(0)
const slideSpring = {
type: spring,
visualDuration: 0.3,
bounce: 0.2,
}
function navigate(direction: 'next' | 'prev') {
startTransition(
() => {
index.value = wrap(
0,
images.length,
index.value + (direction === 'next' ? 1 : -1),
)
},
{ types: [direction] },
)
}
function enterSlide(types: string[]) {
return {
opacity: 1,
transform: [
`translateX(${types.includes('next') ? 100 : -100}%)`,
'translateX(0%)',
],
}
}
function exitSlide(types: string[]) {
return {
opacity: 0,
transform: `translateX(${types.includes('prev') ? 100 : -100}%)`,
}
}
</script>
<template>
<div class="types-example">
<button
class="nav-button"
aria-label="Previous"
@click="navigate('prev')"
>
<ChevronLeft :size="24" />
</button>
<AnimateView
:key="index"
:name="`slide-${index}`"
:transition="slideSpring"
:exit="exitSlide"
:enter="enterSlide"
>
<div class="slide">
<img
:src="images[index]"
:alt="`Prague ${index + 1}`"
class="slide-image"
>
</div>
</AnimateView>
<button
class="nav-button"
aria-label="Next"
@click="navigate('next')"
>
<ChevronRight :size="24" />
</button>
<span class="counter">{{ index + 1 }} / {{ images.length }}</span>
</div>
</template>
<style scoped>
.types-example {
display: flex;
position: relative;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 10px;
width: 100%;
height: 100%;
}
.counter {
width: 100%;
text-align: center;
font-size: 14px;
color: var(--feint-text);
}
.slide {
width: 400px;
height: 300px;
border-radius: 10px;
overflow: hidden;
}
.slide-image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.nav-button {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 1;
outline-offset: 2px;
border: none;
cursor: pointer;
color: white;
background-color: rgba(255, 255, 255, 0.15);
}
.nav-button:hover {
background-color: rgba(255, 255, 255, 0.25);
}
</style>