167 lines
7.1 KiB
TypeScript
167 lines
7.1 KiB
TypeScript
/* eslint-disable react-hooks/rules-of-hooks */
|
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
import { motion } from "framer-motion";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
|
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
|
import { useSwipeable } from "react-swipeable";
|
|
|
|
function VideoSliderMobile() {
|
|
const [items] = useState<any[]>([
|
|
{
|
|
title: "Виртуальный тур по жилому комплексу",
|
|
desc: "Клиент лично оценит угол инсоляции, малые архитектурные формы и ландшафт, перемещаясь по комплексу с помощью тапа.",
|
|
video: "/videos/features/virtual_tour.mp4",
|
|
},
|
|
{
|
|
title: "Вся инфрастуктура на одном экране",
|
|
desc: "Возможность оценить инфраструктуру района покажет важные для клиента точки интереса и время, за которое он сможет до них дойти.",
|
|
video: "/videos/features/nks_infra.mp4",
|
|
},
|
|
{
|
|
title: "Конфигуратор интерьера",
|
|
desc: "Клиент может свободно выбирать мебель и дизайн с помощью конфигуратора интерьера. Возможно выбрать стиль всей квартиры или изменить отдельные детали.",
|
|
video: "/videos/features/uralsky.mp4",
|
|
},
|
|
{
|
|
title: "Параметрический поиск квартир",
|
|
desc: "Фильтр позволит отметить конкретные преимущества, определить количество комнат, желаемый этаж, цену, и получить выборку подходящих вариантов.",
|
|
video: "/videos/features/parametric.mp4",
|
|
},
|
|
{
|
|
title: "Любой рендер за несколько секунд",
|
|
desc: "Когда для рекламы вам понадобится любой объект с любого ракурса, просто сделайте фотографию внутри презентации.",
|
|
video: "/videos/features/render.mp4",
|
|
},
|
|
{
|
|
title: "Формирование вишлиста",
|
|
desc: "Клиент может добавить варианты квартир в избранное, сравнить их между собой по основным параметрам и выбрать свою будущую квартиру.",
|
|
video: "/videos/features/wish.mp4",
|
|
},
|
|
{
|
|
title: "Интеграция с CRM-системой",
|
|
desc: "Приложение передает информацию о клиенте в CRM-систему застройщика и получает актуальную информацию по ценам и статусам квартир.",
|
|
video: "/videos/features/integra_crm.mp4",
|
|
},
|
|
{
|
|
title: "Отправка коммерческого предложения",
|
|
desc: "Коммерческое предложение с выбранными квартирами может быть отправлено клиенту на почту или распечатано и отдано лично в руки.",
|
|
video: "/videos/features/send.mp4",
|
|
},
|
|
// {
|
|
// title: "Интерактивная инсоляция",
|
|
// desc: "Функция позволяет в режиме реального времени увидеть уровень освещенности выбранной квартиры, а если вы изучаете экстерьер жилого комплекса – функция покажет архитектурную подсветку.",
|
|
// video: "",
|
|
// },
|
|
// {
|
|
// title: "Подбор квартир на генплане",
|
|
// desc: "Сделать генплан удобным инструментом выбора квартиры поможет подсветка выбранных квартир прямо на фасаде Жилого комплекса.",
|
|
// video: "",
|
|
// },
|
|
]);
|
|
|
|
const [activeIndex, setActiveIndex] = useState<number>(0);
|
|
const videoRefs = items.map(() => useRef<HTMLVideoElement>(null));
|
|
const handlers = useSwipeable({
|
|
onSwiped: (e) => {
|
|
if (e.dir === "Left") {
|
|
handleClickNext();
|
|
}
|
|
|
|
if (e.dir === "Right") {
|
|
handleClickPrev();
|
|
}
|
|
},
|
|
});
|
|
|
|
function handleClickPrev() {
|
|
if (activeIndex === 0) {
|
|
setActiveIndex(items.length - 1);
|
|
return;
|
|
}
|
|
|
|
setActiveIndex((prev) => prev - 1);
|
|
}
|
|
|
|
function handleClickNext() {
|
|
if (activeIndex === items.length - 1) {
|
|
setActiveIndex(0);
|
|
return;
|
|
}
|
|
|
|
setActiveIndex((prev) => prev + 1);
|
|
}
|
|
|
|
useEffect(() => {
|
|
items.forEach((_, index) => {
|
|
if (activeIndex === index) {
|
|
videoRefs[index].current?.play();
|
|
} else {
|
|
videoRefs[index].current?.pause();
|
|
}
|
|
});
|
|
}, [activeIndex]);
|
|
|
|
return (
|
|
<div
|
|
{...handlers}
|
|
className="xl:hidden flex flex-col sm:gap-6 gap-4 border-b border-[#3D425C] pb-5"
|
|
>
|
|
<div
|
|
// ref={videosContainerRef}
|
|
className={`relative flex sm:gap-[88px] items-start transition-all duration-500`}
|
|
style={{ left: `-${activeIndex * 100}%` }}
|
|
>
|
|
{items.map((item, index) => (
|
|
<motion.video
|
|
key={index}
|
|
ref={videoRefs[index]}
|
|
src={item.video}
|
|
muted
|
|
loop
|
|
playsInline
|
|
preload="metadata"
|
|
className={`relative aspect-video transition-all duration-500 sm:w-[calc(100%-88px)] ${
|
|
index !== activeIndex
|
|
? "sm:scale-[70%] scale-[80%] sm:-translate-y-[15%] -translate-y-[10%] sm:-translate-x-[calc(15%+88px-12px)] -translate-x-[calc(10%-8px)]"
|
|
: ""
|
|
}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex justify-between sm:gap-[30px] gap-3 sm:h-auto h-[168px]">
|
|
<div className="flex flex-col gap-3">
|
|
<p className="text-xl font-gilroy font-medium">
|
|
{items[activeIndex].title}
|
|
</p>
|
|
<p className="text-sm">{items[activeIndex].desc}</p>
|
|
</div>
|
|
<div className="relative flex flex-col items-center gap-4">
|
|
<div className="sm:absolute -top-[64px]">
|
|
<p className="sm:text-2xl text-xl font-gilroy font-medium">
|
|
{activeIndex + 1}/{items.length}
|
|
</p>
|
|
</div>
|
|
<div className="flex flex-col gap-2 self-end">
|
|
<button
|
|
onClick={handleClickNext}
|
|
className="sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none"
|
|
>
|
|
<ArrowRightIcon />
|
|
</button>
|
|
<button
|
|
onClick={handleClickPrev}
|
|
className="sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none"
|
|
>
|
|
<ArrowLeftIcon />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default VideoSliderMobile;
|