history refactoring
This commit is contained in:
+1
-4
@@ -29,14 +29,13 @@ import TelegramIcon from "@components/icons/TelegramIcon";
|
||||
import { IProject } from "../types/IProject";
|
||||
import { SortedProject } from "../types/SortedProject";
|
||||
import { ProjectYear } from "../types/ProjectYear";
|
||||
import Histories from "@components/Histories";
|
||||
import Winners from "@components/Winners";
|
||||
import { getTime, getDate, getYear } from "date-fns";
|
||||
import ProjectsSection from "@components/ProjectsSection";
|
||||
import { motion } from "framer-motion";
|
||||
import { Video } from "../types/Video";
|
||||
import Reviews from "@components/Reviews";
|
||||
import Histories1 from "@components/Histories1";
|
||||
import Histories from "@components/Histories";
|
||||
|
||||
const VIDEOS_FEATURES: Video[] = [
|
||||
{
|
||||
@@ -447,9 +446,7 @@ export default function App() {
|
||||
videos={VIDEOS_FEATURES}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<Histories />
|
||||
<Histories1 />
|
||||
|
||||
<div className="relative 2xl:mb-[200px] sm:mb-[120px] mb-20">
|
||||
<div className="grid sm:grid-cols-2 sm:gap-4 gap-6 2xl:mb-16 xl:mb-10 sm:mb-12 mb-8">
|
||||
|
||||
+220
-207
@@ -1,266 +1,279 @@
|
||||
import { createRef, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { createRef, useEffect, useState } from "react";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { Video } from "../types/Video";
|
||||
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import { Transition } from "react-transition-group";
|
||||
import { motion } from "framer-motion";
|
||||
import { Video } from "../types/Video";
|
||||
|
||||
const VIDEOS: Video[] = [
|
||||
{
|
||||
id: "1",
|
||||
value: "/videos/histories/1.mp4",
|
||||
title: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
value: "/videos/histories/2.mp4",
|
||||
title: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Graff.estate на выставке 100+ TechnoBuild",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
value: "/videos/histories/3.mp4",
|
||||
title: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивная инсталляция graff.estate для ЖК DNS City в г.Владивосток",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/1.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
id: "4",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/2.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Graff.estate на выставке 100+ TechnoBuild",
|
||||
id: "5",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/3.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
id: "6",
|
||||
},
|
||||
];
|
||||
|
||||
const Histories = () => {
|
||||
const [isViewportEntered, setIsViewportEntered] = useState<boolean>(false);
|
||||
|
||||
function handleViewportEnter() {
|
||||
if (isViewportEntered) return;
|
||||
setIsViewportEntered(true);
|
||||
}
|
||||
|
||||
const [videoRefs, setVideoRefs] = useState<
|
||||
React.RefObject<HTMLVideoElement>[]
|
||||
>([]);
|
||||
const [videoProgress, setVideoProgress] = useState(0);
|
||||
const [cardWidth, setCardWidth] = useState(0);
|
||||
function Histories() {
|
||||
const [selectedVideoIndex, setSelectedVideoIndex] = useState(0);
|
||||
const videoRefs = VIDEOS.map(() => createRef<HTMLVideoElement>());
|
||||
const [videoWidth, setVideoWidth] = useState<number>(0);
|
||||
const [videoHeigth, setVideoHeigth] = useState<number>(0);
|
||||
const [videoProgesses, setVideoProgresses] = useState<number[]>([]);
|
||||
const { ref, inView } = useInView();
|
||||
const [isEntered, setIsEntered] = useState(false);
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: handleOnRightMove,
|
||||
onSwipedRight: handleOnLeftMove,
|
||||
onSwipedLeft: next,
|
||||
onSwipedRight: prev,
|
||||
trackMouse: true,
|
||||
});
|
||||
const [selectedVideo, setSelectedVideo] = useState<number>(0);
|
||||
const [_document, setDocument] = useState<Document>();
|
||||
|
||||
function handleOnLeftMove(): void {
|
||||
if (selectedVideo > 0) {
|
||||
setSelectedVideo((prev) => prev - 1);
|
||||
function handleEnded() {
|
||||
if (selectedVideoIndex === VIDEOS.length - 1) {
|
||||
setSelectedVideoIndex(0);
|
||||
} else {
|
||||
setSelectedVideoIndex((prev) => prev + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function handleOnRightMove(): void {
|
||||
if (selectedVideo < 2) {
|
||||
setSelectedVideo((prev) => prev + 1);
|
||||
}
|
||||
function prev() {
|
||||
if (selectedVideoIndex === 0) return;
|
||||
setSelectedVideoIndex((prev) => prev - 1);
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (selectedVideoIndex === VIDEOS.length - 1) return;
|
||||
setSelectedVideoIndex((prev) => prev + 1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setDocument(document);
|
||||
|
||||
setVideoRefs((elRefs) =>
|
||||
Array.from({ length: VIDEOS.length }, (_, index) => index).map(
|
||||
(_, i) => elRefs[i] || createRef()
|
||||
)
|
||||
);
|
||||
const progresses = VIDEOS.map(() => 0);
|
||||
setVideoProgresses(progresses);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!_document) return;
|
||||
|
||||
const clientWidth = _document.children[0].clientWidth;
|
||||
|
||||
if (clientWidth >= 1600) {
|
||||
setCardWidth(420);
|
||||
} else if (clientWidth >= 1280) {
|
||||
setCardWidth(379);
|
||||
} else if (clientWidth >= 640) {
|
||||
setCardWidth(370);
|
||||
} else {
|
||||
setCardWidth(clientWidth - 16);
|
||||
}
|
||||
}, [_document]);
|
||||
|
||||
useEffect(() => {
|
||||
videoRefs.forEach((video, index) => {
|
||||
if (!video.current) return;
|
||||
if (index === selectedVideo) {
|
||||
video.current.play();
|
||||
} else {
|
||||
video.current.pause();
|
||||
}
|
||||
});
|
||||
}, [videoRefs, selectedVideo, isViewportEntered]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentVideoRef = videoRefs[selectedVideo];
|
||||
if (!currentVideoRef || !currentVideoRef.current) return;
|
||||
|
||||
const progress = Math.ceil(
|
||||
(100 / currentVideoRef.current.duration) *
|
||||
currentVideoRef.current.currentTime
|
||||
);
|
||||
setVideoProgress(progress);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (!currentVideoRef || !currentVideoRef.current) return;
|
||||
const progress =
|
||||
(100 / currentVideoRef.current.duration) *
|
||||
currentVideoRef.current.currentTime;
|
||||
const updatedProgresses = videoRefs.map((currentVideoRef) => {
|
||||
if (!currentVideoRef.current) return 0;
|
||||
|
||||
setVideoProgress(progress);
|
||||
if (97 <= progress) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
return (
|
||||
(100 / currentVideoRef.current.duration) *
|
||||
currentVideoRef.current.currentTime
|
||||
);
|
||||
});
|
||||
|
||||
setVideoProgresses(updatedProgresses);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [selectedVideo, videoRefs]);
|
||||
}, [selectedVideoIndex, videoRefs]);
|
||||
|
||||
function handleOnVideoEnd(): void {
|
||||
if (selectedVideo >= videoRefs.length - 1) {
|
||||
setSelectedVideo(0);
|
||||
} else {
|
||||
setSelectedVideo((prev) => prev + 1);
|
||||
useEffect(() => {
|
||||
if (!inView || isEntered) return;
|
||||
|
||||
setIsEntered(true);
|
||||
}, [inView]);
|
||||
|
||||
useEffect(() => {
|
||||
for (let index = 0; index < videoRefs.length; index++) {
|
||||
if (selectedVideoIndex === index) {
|
||||
videoRefs[index].current?.play();
|
||||
} else {
|
||||
videoRefs[index].current?.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isEntered, selectedVideoIndex]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
onViewportEnter={handleViewportEnter}
|
||||
className="container mx-auto 2xl:max-w-screen-2xl flex flex-col justify-center 2xl:mb-[200px] sm:mb-[120px] mb-20"
|
||||
<div
|
||||
ref={ref}
|
||||
className="container mx-auto 2xl:max-w-screen-2xl mb-[120px]"
|
||||
>
|
||||
<div className="sm:border-b border-b-[rgb(61,66,92)] 2xl:h-[760px] xl:h-[687px] sm:h-[656px] h-full">
|
||||
<div className="flex sm:h-full h-fit sm:flex-row flex-col sm:w-full sm:mx-0 mx-auto">
|
||||
<div className=" flex flex-col justify-between 2xl:min-w-[384px] xl:min-w-[308px] sm:min-w-[263px] sm:pb-6 xl:pb-10 sm:h-full">
|
||||
<div className="2xl:pr-10 xl:pr-8 sm:pr-[37px]">
|
||||
<h2 className="font-medium 2xl:text-[64px] xl:text-5xl text-[40px] leading-10 sm:mb-10 mb-6 font-gilroy">
|
||||
<p className="from-[#798FFF] to-[#D375FF] bg-gradient-to-r bg-clip-text text-transparent">
|
||||
Истории
|
||||
</p>
|
||||
<p>graff.estate</p>
|
||||
</h2>
|
||||
|
||||
{VIDEOS.map((item, index) => (
|
||||
<Transition
|
||||
key={index}
|
||||
in={Boolean(index === selectedVideo)}
|
||||
timeout={300}
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute 2xl:w-[344px] xl:w-[276px] sm:w-[226px] w-[280px] transition-opacity duration-300 ${state}`}
|
||||
>
|
||||
<p className="font-semibold mb-4 2xl:text-xl xl:text-[16px] sm:block hidden">
|
||||
<div className="flex 2xl:h-[760px] xl:h-[687px] sm:h-[500px] h-[875px] sm:border-b border-b-[#3D425C] sm:flex-row flex-col">
|
||||
<div className="left 2xl:min-w-[496px] xl:min-w-[395px] sm:min-w-[354px] flex flex-col sm:pb-10 pb-6">
|
||||
<h2 className="font-medium 2xl:text-[64px] xl:text-5xl text-[40px] sm:mb-10 mb-6 font-gilroy leading-10">
|
||||
<p className="from-[#798FFF] to-[#D375FF] bg-gradient-to-r bg-clip-text text-transparent">
|
||||
Истории
|
||||
</p>
|
||||
<p>graff.estate</p>
|
||||
</h2>
|
||||
<div className="relative bg-white sm:block hidden">
|
||||
{VIDEOS.map((item, index) => (
|
||||
<Transition
|
||||
key={index}
|
||||
in={Boolean(index === selectedVideoIndex)}
|
||||
timeout={300}
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute transition-opacity duration-300 pr-10 ${state}`}
|
||||
>
|
||||
{item.title ? (
|
||||
<p className="font-semibold mb-4 2xl:text-xl xl:text-[16px]">
|
||||
{item.title}
|
||||
</p>
|
||||
<p className="font-normal 2xl:text-[16px] xl:text-[14px] sm:block hidden">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
<div className="gap-2 sm:flex hidden">
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={handleOnLeftMove}
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</button>
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={handleOnRightMove}
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative sm:border-l border-l-[#3D425C] sm:h-full h-[548px] w-full">
|
||||
<div className="absolute overflow-x-clip h-full ">
|
||||
<div {...handlers} className="absolute h-full w-full z-10" />
|
||||
<div
|
||||
className="flex gap-4 w-full h-full transition-all duration-300 ease-in-out 2xl:pl-10 xl:pl-8 sm:pl-3 xl:pb-10 sm:pb-[25px] "
|
||||
style={{
|
||||
transform: `translateX(${-selectedVideo * cardWidth}px)`,
|
||||
}}
|
||||
>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className={`relative 2xl:w-[404px] xl:w-[363px] sm:w-[354px] w-[calc(100vw-32px)] 2xl:h-[720px] xl:h-[647px] sm:h-[632px] h-[546px] transition-opacity duration-300 ${
|
||||
selectedVideo === index ? "opacity-100" : "opacity-60"
|
||||
}`}
|
||||
>
|
||||
<video
|
||||
poster={"/images/posters/integra_crm.jpg"}
|
||||
// poster={video.poster}
|
||||
ref={videoRefs[index]}
|
||||
src={isViewportEntered ? video.value : ""}
|
||||
muted
|
||||
// loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
className="w-full h-full object-cover touch-none"
|
||||
onEnded={handleOnVideoEnd}
|
||||
// onEnded={() => console.log("first")}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={`absolute bottom-0 w-full h-1 bg-[#52587A] sm:block hidden transition-opacity duration-500 ${
|
||||
selectedVideo === index ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="w-0 bg-white h-1 transition-[width] duration-500"
|
||||
style={{ width: `${videoProgress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<p className="font-normal 2xl:text-[16px] xl:text-[14px]">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className=" p-6 block sm:hidden mb-6">
|
||||
<h2 className="font-semibold text-sm leading-[18px] pb-2">
|
||||
{VIDEOS[selectedVideo].title}
|
||||
</h2>
|
||||
<p className="text-[12px] leading-[18px]">
|
||||
{VIDEOS[selectedVideo].desc}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mx-auto flex gap-2 h-2 sm:hidden">
|
||||
<div className="bg-[#a1a2a6] w-10 h-full rounded-full overflow-hidden">
|
||||
<div
|
||||
className="bg-white w-0 h-full rounded-full transition-all duration-500"
|
||||
style={{ width: `${videoProgress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="h-full w-2 rounded-full transition-all duration-300"
|
||||
style={{
|
||||
background: `${
|
||||
selectedVideo === index ? "white" : "#a1a2a6"
|
||||
}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
<div className="gap-2 hidden sm:flex mt-auto">
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={prev}
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</button>
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={next}
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
{...handlers}
|
||||
className="right relative sm:border-l border-l-[#3D425C] flex gap-4 sm:pb-10 sm:pl-10 select-none"
|
||||
style={{
|
||||
clipPath: `rect(auto auto auto 0)`,
|
||||
}}
|
||||
>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="relative sm:aspect-[9/16] aspect-auto bg-black transition-transform duration-300 sm:h-auto sm:min-w-0 h-[546px] min-w-[100vw]"
|
||||
style={{
|
||||
transform: `translateX(-${
|
||||
videoWidth * selectedVideoIndex + 16 * selectedVideoIndex
|
||||
}px)`,
|
||||
}}
|
||||
>
|
||||
<video
|
||||
ref={videoRefs[index]}
|
||||
src={isEntered ? video.value : undefined}
|
||||
poster={video.poster}
|
||||
muted
|
||||
playsInline
|
||||
className="aspect-[9/16] object-cover w-full h-full min-w-screen"
|
||||
onEnded={handleEnded}
|
||||
onLoadedData={() =>
|
||||
setTimeout(() => {
|
||||
setVideoWidth(videoRefs[0].current!.clientWidth);
|
||||
setVideoHeigth(videoRefs[0].current!.clientHeight);
|
||||
}, 200)
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className={`absolute progress-bar h-[3px] bg-[#52587A] transition-opacity duration-500 sm:block hidden ${
|
||||
selectedVideoIndex === index ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
style={{
|
||||
width: `${videoWidth}px`,
|
||||
top: `${videoHeigth - 3}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="bg-white h-full transition-[width] duration-300"
|
||||
style={{
|
||||
width: `${videoProgesses[index]}%`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="relative block sm:hidden h-[100%] bg-[#212431] p-6">
|
||||
{VIDEOS.map((item, index) => (
|
||||
<Transition
|
||||
key={index}
|
||||
in={Boolean(index === selectedVideoIndex)}
|
||||
timeout={300}
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute transition-opacity duration-300 pr-10 ${state}`}
|
||||
>
|
||||
{item.title ? (
|
||||
<p className="font-semibold mb-4 2xl:text-xl xl:text-[16px]">
|
||||
{item.title}
|
||||
</p>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<p className="font-normal 2xl:text-[16px] xl:text-[14px]">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
<div className="mx-auto flex gap-2 min-h-2 sm:hidden relative mt-6">
|
||||
<div className="bg-[#a1a2a6] w-10 h-full rounded-full overflow-hidden">
|
||||
<div
|
||||
className="bg-white w-0 h-full rounded-full transition-all duration-500"
|
||||
style={{ width: `${videoProgesses[selectedVideoIndex]}%` }}
|
||||
></div>
|
||||
</div>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="h-full w-2 rounded-full transition-all duration-300"
|
||||
style={{
|
||||
background: `${
|
||||
selectedVideoIndex === index ? "white" : "#a1a2a6"
|
||||
}`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default Histories;
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
import { createRef, useEffect, useState } from "react";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { Video } from "../types/Video";
|
||||
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import { Transition } from "react-transition-group";
|
||||
|
||||
const VIDEOS: Video[] = [
|
||||
{
|
||||
id: "1",
|
||||
value: "/videos/histories/1.mp4",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
value: "/videos/histories/2.mp4",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Graff.estate на выставке 100+ TechnoBuild",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
value: "/videos/histories/3.mp4",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивная инсталляция graff.estate для ЖК DNS City в г.Владивосток",
|
||||
poster: "",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/1.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
id: "4",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/2.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Graff.estate на выставке 100+ TechnoBuild",
|
||||
id: "5",
|
||||
},
|
||||
{
|
||||
value: "https://graff.estate/videos/histories/3.mp4",
|
||||
poster: "",
|
||||
title: "Вся информация о жилом комплексе на одном экране",
|
||||
desc: "Интерактивный комплекс GRAFF.estate для ЖК Upside Towers, Москва",
|
||||
id: "6",
|
||||
},
|
||||
];
|
||||
|
||||
function Histories1() {
|
||||
const [selectedVideoIndex, setSelectedVideoIndex] = useState(0);
|
||||
const videoRefs = VIDEOS.map(() => createRef<HTMLVideoElement>());
|
||||
const [videoWidth, setVideoWidth] = useState<number>(0);
|
||||
const [videoHeigth, setVideoHeigth] = useState<number>(0);
|
||||
// const [_document, setDocument] = useState<Document>();
|
||||
const { ref, inView } = useInView();
|
||||
const [isEntered, setIsEntered] = useState(false);
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: next,
|
||||
onSwipedRight: prev,
|
||||
trackMouse: true,
|
||||
});
|
||||
|
||||
function handleEnded() {
|
||||
if (selectedVideoIndex === VIDEOS.length - 1) {
|
||||
setSelectedVideoIndex(0);
|
||||
} else {
|
||||
setSelectedVideoIndex((prev) => prev + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function prev() {
|
||||
if (selectedVideoIndex === 0) return;
|
||||
setSelectedVideoIndex((prev) => prev - 1);
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (selectedVideoIndex === VIDEOS.length - 1) return;
|
||||
setSelectedVideoIndex((prev) => prev + 1);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!inView || isEntered) return;
|
||||
|
||||
setIsEntered(true);
|
||||
}, [inView]);
|
||||
|
||||
useEffect(() => {
|
||||
for (let index = 0; index < videoRefs.length; index++) {
|
||||
if (selectedVideoIndex === index) {
|
||||
videoRefs[index].current?.play();
|
||||
} else {
|
||||
videoRefs[index].current?.pause();
|
||||
}
|
||||
}
|
||||
}, [isEntered, selectedVideoIndex]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="container mx-auto 2xl:max-w-screen-2xl">
|
||||
<div className="flex 2xl:h-[760px] xl:h-[687px] sm:h-[500px] h-[875px] sm:border-b border-b-[#3D425C] sm:flex-row flex-col">
|
||||
<div className="left 2xl:min-w-[496px] xl:min-w-[395px] sm:min-w-[354px] flex flex-col sm:pb-10 pb-6">
|
||||
<h2 className="font-medium 2xl:text-[64px] xl:text-5xl text-[40px] sm:mb-10 mb-6 font-gilroy leading-10">
|
||||
<p className="from-[#798FFF] to-[#D375FF] bg-gradient-to-r bg-clip-text text-transparent">
|
||||
Истории
|
||||
</p>
|
||||
<p>graff.estate</p>
|
||||
</h2>
|
||||
<div className="relative bg-white sm:block hidden">
|
||||
{VIDEOS.map((item, index) => (
|
||||
<Transition
|
||||
key={index}
|
||||
in={Boolean(index === selectedVideoIndex)}
|
||||
timeout={300}
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute transition-opacity duration-300 pr-10 ${state}`}
|
||||
>
|
||||
{item.title ? (
|
||||
<p className="font-semibold mb-4 2xl:text-xl xl:text-[16px]">
|
||||
{item.title}
|
||||
</p>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<p className="font-normal 2xl:text-[16px] xl:text-[14px]">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
<div className="gap-2 hidden sm:flex mt-auto">
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={prev}
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</button>
|
||||
<button
|
||||
className="p-4 border border-[#3D425C] rounded-full"
|
||||
onClick={next}
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
{...handlers}
|
||||
className="right relative sm:border-l border-l-[#3D425C] flex gap-4 sm:pb-10 sm:pl-10 select-none"
|
||||
style={{
|
||||
clipPath: `rect(auto auto auto 0)`,
|
||||
}}
|
||||
>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="relative sm:aspect-[9/16] aspect-auto bg-black transition-transform duration-300 sm:h-auto sm:min-w-0 h-[546px] min-w-[100vw]"
|
||||
style={{
|
||||
transform: `translateX(-${
|
||||
videoWidth * selectedVideoIndex + 16 * selectedVideoIndex
|
||||
}px)`,
|
||||
}}
|
||||
>
|
||||
<video
|
||||
ref={videoRefs[index]}
|
||||
src={isEntered ? video.value : undefined}
|
||||
poster={video.poster}
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
className="aspect-[9/16] object-cover w-full h-full min-w-screen"
|
||||
onEnded={handleEnded}
|
||||
onLoadedData={() =>
|
||||
setTimeout(() => {
|
||||
setVideoWidth(videoRefs[0].current!.clientWidth);
|
||||
setVideoHeigth(videoRefs[0].current!.clientHeight);
|
||||
}, 200)
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className={`absolute progress-bar h-[3px] bg-[#52587A] transition-opacity duration-700 ${
|
||||
selectedVideoIndex === index ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
style={{
|
||||
width: `${videoWidth}px`,
|
||||
top: `${videoHeigth - 3}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="bg-white h-full"
|
||||
style={{
|
||||
width: `10%`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="relative block sm:hidden h-[100%] bg-[#212431] p-6">
|
||||
{VIDEOS.map((item, index) => (
|
||||
<Transition
|
||||
key={index}
|
||||
in={Boolean(index === selectedVideoIndex)}
|
||||
timeout={300}
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute transition-opacity duration-300 pr-10 ${state}`}
|
||||
>
|
||||
{item.title ? (
|
||||
<p className="font-semibold mb-4 2xl:text-xl xl:text-[16px]">
|
||||
{item.title}
|
||||
</p>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<p className="font-normal 2xl:text-[16px] xl:text-[14px]">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
<div className="mx-auto flex gap-2 min-h-2 sm:hidden">
|
||||
<div className="bg-[#a1a2a6] w-10 h-full rounded-full overflow-hidden">
|
||||
<div
|
||||
className="bg-white w-0 h-auto rounded-full transition-all duration-500"
|
||||
style={{ width: `${20}%` }}
|
||||
></div>
|
||||
</div>
|
||||
{VIDEOS.map((video, index) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="h-full w-2 rounded-full transition-all duration-300"
|
||||
style={{
|
||||
background: `${1 === index ? "white" : "#a1a2a6"}`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Histories1;
|
||||
Reference in New Issue
Block a user