This commit is contained in:
marzban-dev
2025-05-30 15:39:26 +03:30
parent c0190953b7
commit d9d6006c2e
+186 -113
View File
@@ -2,135 +2,208 @@
// import // import
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
import { motion } from "motion-v";
// state // state
const { data: homeData } = useHomeData(); const { data: homeData } = useHomeData();
const { $gsap: gsap, $ScrollTrigger: ScrollTrigger } = useNuxtApp(); const activeSlide = ref(0);
let gsapTimeline: gsap.core.Timeline; const variants = {
let scrollTrigger: ScrollTrigger; hide: { opacity: 0, y: -200 },
show: {
opacity: 1,
y: 0,
transition: {
when: "beforeChildren",
staggerChildren: 0.15,
},
},
exit: (slidesCount: number) => {
return {
opacity: 0,
y: 200,
transition: {
when: "afterChildren",
delay: slidesCount * 0.21,
staggerChildren: 0.1,
staggerDirection: 1,
},
};
},
};
// lifecycle const childContentVariants = {
hide: {
filter: "blur(20px)",
opacity: 0,
},
show: {
filter: "blur(0px)",
opacity: 1,
},
exit: {
filter: "blur(20px)",
opacity: 0,
},
};
const childImageVariants = {
hide: {
filter: "blur(20px)",
y: 70,
scale: 0.65,
opacity: 0,
},
show: {
filter: "blur(0px)",
y: 0,
scale: 1,
opacity: 1,
transition: {
type: "spring",
damping: 20,
},
},
exit: {
filter: "blur(20px)",
y: 70,
scale: 0.65,
opacity: 0,
transition: {
default: {
type: "spring",
damping: 20,
},
opacity: {
duration: 0.1,
},
},
},
};
const nextSlide = () => {
const slidesCount = homeData.value!.show_case_slider.length;
if (activeSlide.value > slidesCount - 2) {
activeSlide.value = 0;
} else {
activeSlide.value = activeSlide.value + 1;
}
};
onMounted(() => { onMounted(() => {
gsapTimeline = gsap.timeline(); nextSlide();
const showcaseElements = gsap.utils.toArray<HTMLElement>(".showcase-slide");
setTimeout(() => { setInterval(() => {
showcaseElements.forEach((element, index) => { nextSlide();
gsapTimeline.fromTo( }, 5000);
element,
index === 0
? {
opacity: 1,
scale: 1,
// rotateX: -25,
zIndex: 1,
top: 0,
ease: "none",
}
: {
opacity: 0,
scale: 1,
// rotateX: -25,
zIndex: 1,
top: 20,
ease: "none",
},
{
opacity: 1,
scale: 1,
// rotateX: 0,
zIndex: 5,
top: 0,
ease: "none",
},
index === 0 ? "-=0%" : undefined
);
if (index < showcaseElements.length - 1) {
gsapTimeline.to(element, {
opacity: 0,
scale: 1.03,
// rotateX: 25,
top: -20,
ease: "none",
});
}
});
scrollTrigger = ScrollTrigger.create({
trigger: "#products-showcase-container",
animation: gsapTimeline,
scrub: 1,
pin: true,
start: "top top",
anticipatePin: 1,
// markers: true,
end: "bottom top",
});
setTimeout(() => {
scrollTrigger.update();
scrollTrigger.refresh();
}, 1000);
}, 1000);
});
onUnmounted(() => {
gsapTimeline.progress(1).pause();
gsapTimeline.kill();
}); });
</script> </script>
<template> <template>
<section <section class="relative z-[999] h-[115svh] min-h-[1000px] pb-20 bg-black overflow-y-hidden flex-center">
id="products-showcase-container" <AnimatePresence mode="popLayout">
class="perspective-midrange relative z-[999]" <template
>
<div class="w-full min-h-[120svh] lg:min-h-[102svh] bg-black">
<div
v-for="(slide, index) in homeData!.show_case_slider" v-for="(slide, index) in homeData!.show_case_slider"
:key="index" :key="slide.id"
class="showcase-slide origin-bottom absolute size-full bg-black flex items-center justify-center max-lg:-mt-16 lg:mt-5"
> >
<NuxtImg <motion.div
class="w-[280px] xs:w-[350px] lg:w-[500px] xl:w-[650px] z-20 mb-30 sm:mb-20 lg:mb-30" v-if="activeSlide === index"
:src="slide.image" :initial="{ opacity: 0 }"
:style="{ :animate="{ opacity: 1 }"
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0) 100%)', :exit="{ opacity: 0 }"
}" :transition="{ duration: 1 }"
alt="" class="absolute size-full inset-0 -z-10"
/> >
<div class="flex flex-col items-center justify-center gap-6 text-center absolute z-20 mt-20"> <NuxtImg
<span class="text-white typo-h-6 sm:typo-h-5 lg:typo-h-4 xl:typo-h-3"> :src="slide.background_image"
{{ slide.title }} class="absolute size-full object-cover"
</span> />
<p </motion.div>
class="text-white max-w-[320px] xs:max-w-[360px] sm:max-w-[480px] lg:max-w-[550px] xl:max-w-[750px] typo-p-sm lg:typo-p-md xl:typo-p-lg" </template>
> </AnimatePresence>
{{ slide.description }}
</p> <AnimatePresence mode="popLayout">
<NuxtLink <template
:to="`/resellers/category/${slide.id}`" v-for="(slide, index) in homeData!.show_case_slider"
class="relative" :key="slide.id"
> >
<NuxtImg <motion.div
src="/img/heymlz/heymlz-falling.gif" v-if="activeSlide === index"
class="absolute top-[101px] sm:top-[100px] lg:top-[117px] left-1/2 -translate-1/2 w-[200px] lg:w-[250px] drop-shadow-md" :custom="homeData!.show_case_slider.length"
/> :variants="variants"
<Button initial="hide"
variant="primary" animate="show"
end-icon="ci:arrow-left" exit="exit"
class="mt-8 max-sm:py-2 max-lg:typo-label-xs px-10 rounded-full hover:bg-transparent" class="size-full flex flex-col gap-20 items-center justify-center"
>
<div class="flex items-center gap-6 perspective-midrange">
<motion.div
:variants="childImageVariants"
class="origin-bottom-left"
> >
مشاهده دسته بندی <NuxtImg
</Button> class="w-[300px] z-20 mt-40"
</NuxtLink> :src="slide.image3"
</div> alt=""
</div> />
</div> </motion.div>
<motion.div
:variants="childImageVariants"
class="origin-bottom"
>
<NuxtImg
class="w-[300px] z-20"
:src="slide.image2"
alt=""
/>
</motion.div>
<motion.div
:variants="childImageVariants"
class="origin-bottom-right"
>
<NuxtImg
class="w-[300px] z-20 mt-40"
:src="slide.image1"
alt=""
/>
</motion.div>
</div>
<motion.div
:variants="childContentVariants"
class="flex flex-col items-center justify-center gap-6 text-center"
>
<span class="text-white typo-h-6 sm:typo-h-5 lg:typo-h-4 xl:typo-h-3">
{{ slide.title }}
</span>
<p
class="text-white max-w-[320px] xs:max-w-[360px] sm:max-w-[480px] lg:max-w-[550px] xl:max-w-[750px] typo-p-sm lg:typo-p-md xl:typo-p-lg"
>
{{ slide.description }}
</p>
<NuxtLink
:to="`/resellers/category/${slide.id}`"
class="relative"
>
<NuxtImg
src="/img/heymlz/heymlz-falling.gif"
class="absolute top-[101px] sm:top-[100px] lg:top-[117px] left-1/2 -translate-1/2 w-[200px] lg:w-[250px] drop-shadow-md"
/>
<Button
variant="primary"
end-icon="ci:arrow-left"
class="mt-8 max-sm:py-2 max-lg:typo-label-xs px-10 rounded-full hover:bg-transparent"
>
مشاهده دسته بندی
</Button>
</NuxtLink>
</motion.div>
</motion.div>
</template>
</AnimatePresence>
</section> </section>
</template> </template>