136 lines
4.4 KiB
TypeScript
136 lines
4.4 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import Image from "next/image";
|
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
|
|
// Типы для свойств компонента
|
|
interface HeroProps {
|
|
images?: string[];
|
|
}
|
|
|
|
export default function Hero({ images = [] }: HeroProps) {
|
|
// Состояние для текущего индекса слайда
|
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
// Состояние для автоматического воспроизведения
|
|
const [autoplay, setAutoplay] = useState(true);
|
|
// Состояние для направления анимации: 1 - следующий слайд, -1 - предыдущий слайд
|
|
const [direction, setDirection] = useState(0);
|
|
|
|
// Если изображения не переданы или массив пуст, используем стандартное изображение
|
|
const heroImages = images.length > 0
|
|
? images
|
|
: ['/photos/head_photo.png'];
|
|
|
|
// Функция для перехода к следующему слайду
|
|
const nextSlide = () => {
|
|
setDirection(1);
|
|
setCurrentIndex((prevIndex) =>
|
|
prevIndex === heroImages.length - 1 ? 0 : prevIndex + 1
|
|
);
|
|
};
|
|
|
|
// Функция для перехода к предыдущему слайду
|
|
const prevSlide = () => {
|
|
setDirection(-1);
|
|
setCurrentIndex((prevIndex) =>
|
|
prevIndex === 0 ? heroImages.length - 1 : prevIndex - 1
|
|
);
|
|
};
|
|
|
|
// Эффект для автоматического воспроизведения слайдера
|
|
useEffect(() => {
|
|
let interval: NodeJS.Timeout;
|
|
|
|
if (autoplay) {
|
|
interval = setInterval(() => {
|
|
nextSlide();
|
|
}, 5000); // Смена слайда каждые 5 секунд
|
|
}
|
|
|
|
return () => {
|
|
if (interval) clearInterval(interval);
|
|
};
|
|
}, [autoplay, currentIndex]);
|
|
|
|
// Определяем варианты анимации для эффекта "скольжения"
|
|
const variants = {
|
|
enter: (direction: number) => ({
|
|
x: direction > 0 ? "100%" : "-100%",
|
|
opacity: 0
|
|
}),
|
|
center: {
|
|
x: 0,
|
|
opacity: 1
|
|
},
|
|
exit: (direction: number) => ({
|
|
x: direction > 0 ? "-100%" : "100%",
|
|
opacity: 0
|
|
}),
|
|
};
|
|
|
|
return (
|
|
<div className="relative h-screen overflow-hidden">
|
|
<AnimatePresence custom={direction} mode="wait">
|
|
<motion.div
|
|
key={currentIndex}
|
|
custom={direction}
|
|
variants={variants}
|
|
initial="enter"
|
|
animate="center"
|
|
exit="exit"
|
|
transition={{ duration: 0.7 }}
|
|
className="absolute inset-0"
|
|
>
|
|
<Image
|
|
src={heroImages[currentIndex]}
|
|
alt="New Arrivals"
|
|
layout="fill"
|
|
objectFit="cover"
|
|
quality={100}
|
|
priority
|
|
/>
|
|
{/* Убрали затемнение изображения */}
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
|
|
<button
|
|
className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-white bg-opacity-50 p-2 rounded-full hover:bg-opacity-70 transition-all"
|
|
onClick={() => {
|
|
prevSlide();
|
|
setAutoplay(false);
|
|
}}
|
|
>
|
|
<ChevronLeft className="w-6 h-6 text-black" />
|
|
</button>
|
|
|
|
<button
|
|
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-white bg-opacity-50 p-2 rounded-full hover:bg-opacity-70 transition-all"
|
|
onClick={() => {
|
|
nextSlide();
|
|
setAutoplay(false);
|
|
}}
|
|
>
|
|
<ChevronRight className="w-6 h-6 text-black" />
|
|
</button>
|
|
|
|
{/* Индикаторы слайдов */}
|
|
<div className="absolute bottom-8 left-0 right-0 flex justify-center space-x-2">
|
|
{heroImages.map((_, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={() => {
|
|
// Определяем направление в зависимости от выбранного индекса
|
|
setDirection(index > currentIndex ? 1 : -1);
|
|
setCurrentIndex(index);
|
|
setAutoplay(false);
|
|
}}
|
|
className={`w-3 h-3 rounded-full ${
|
|
index === currentIndex ? 'bg-white' : 'bg-white/50'
|
|
}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|