dml_frontend/components/ArticleCarousel.vue

171 lines
3.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- components/ArticleCarousel.vue -->
<template>
<div class="carousel-wrapper">
<button class="nav-button left" @click="prev"></button>
<div class="carousel">
<transition-group
:name="directionClass"
tag="div"
class="carousel-inner"
>
<div
v-for="(article, index) in visibleItems"
:key="article.id + '-' + currentIndex"
class="carousel-item"
>
<ArticleCard
:header="article.header"
:image="article.image"
:link="localePath({ name: 'article-link', params: { link: article.slug } })"
:readmore-text="$t('pages.magazin.readmore')"
/>
</div>
</transition-group>
</div>
<button class="nav-button right" @click="next"></button>
</div>
</template>
<script setup lang="ts">
import ArticleCard from './ArticleCard.vue'
import { useLocalePath } from '#i18n'
const props = defineProps({
articles: {
type: Array,
required: true
}
})
const localePath = useLocalePath()
const currentIndex = ref(0)
const itemsPerView = ref(3)
const direction = ref<'left' | 'right'>('right')
const directionClass = computed(() =>
direction.value === 'left' ? 'slide-left' : 'slide-right'
)
const updateItemsPerView = () => {
const width = window.innerWidth
if (width < 600) itemsPerView.value = 1
else if (width < 1024) itemsPerView.value = 2
else itemsPerView.value = 3
}
onMounted(() => {
updateItemsPerView()
window.addEventListener('resize', updateItemsPerView)
})
const visibleItems = computed(() => {
const result = []
for (let i = 0; i < itemsPerView.value; i++) {
const index = (currentIndex.value + i) % props.articles.length
result.push(props.articles[index])
}
return result
})
function prev() {
direction.value = 'left'
currentIndex.value = (currentIndex.value - 1 + props.articles.length) % props.articles.length
}
function next() {
direction.value = 'right'
currentIndex.value = (currentIndex.value + 1) % props.articles.length
}
</script>
<style lang="sass">
.carousel-wrapper
position: relative
display: flex
align-items: center
justify-content: center
gap: 1rem
.nav-button
all: unset
cursor: pointer
font-size: 4rem
color: lighten($darkgrey, 20%)
font-weight: bold
padding: 0.5rem 1rem
border-radius: 1rem
background-image: radial-gradient(white, transparent)
transition: 0.3s
&:hover
background-image: radial-gradient($beige, transparent)
.carousel
overflow-x: clip
overflow-y: visible
max-width: 100%
flex: 1
.carousel-inner
display: flex
gap: 2rem
transition: all 0.5s ease
align-items: stretch
.carousel-item
flex: 1 0 calc(100% / 3)
min-width: 0
transition: transform 0.3s ease
display: flex
// Slide nach rechts (Nächste)
.slide-right-enter-active
transition: transform 0.6s ease, opacity 0.6s ease
.slide-right-enter-from
transform: translateX(100%) scale(0.8)
opacity: 0
.slide-right-enter-to
transform: translateX(0) scale(1)
opacity: 1
.slide-right-leave-active
transition: transform 0.4s ease
.slide-right-leave-from
transform: translateX(0)
opacity: 1
.slide-right-leave-to
transform: translateX(-100%)
opacity: 1
// Slide nach links (Vorherige)
.slide-left-enter-active
transition: transform 0.6s ease, opacity 0.6s ease
.slide-left-enter-from
transform: translateX(-100%) scale(0.8)
opacity: 0
.slide-left-enter-to
transform: translateX(0) scale(1)
opacity: 1
.slide-left-leave-active
transition: transform 0.4s ease
.slide-left-leave-from
transform: translateX(0)
opacity: 1
.slide-left-leave-to
transform: translateX(100%)
opacity: 1
</style>