lazy loding
This commit is contained in:
+46
-12
@@ -36,6 +36,17 @@ import LabelCard from "@components/CardYear";
|
||||
import ProjectsSection from "@components/ProjectsSection";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const VIDEOS = [
|
||||
"/videos/features/virtual_tour.mp4",
|
||||
"/videos/features/nks_infra.mp4",
|
||||
"/videos/features/uralsky.mp4",
|
||||
"/videos/features/parametric.mp4",
|
||||
"/videos/features/render.mp4",
|
||||
"/videos/features/wish.mp4",
|
||||
"/videos/features/integra_crm.mp4",
|
||||
"/videos/features/send.mp4",
|
||||
];
|
||||
|
||||
function getSortedProjects(projects: IProject[]) {
|
||||
const sorted = [...projects].sort(
|
||||
(da, db) => getTime(db.releaseDate) - getTime(da.releaseDate)
|
||||
@@ -87,7 +98,33 @@ export default function App() {
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
const [isShownAllProjects, setIsShownAllProjects] = useState<boolean>(false);
|
||||
const [isBuffering, setIsBuffering] = useState(true);
|
||||
const featureRef = useRef(null);
|
||||
const [viewPortEntered, setViewPortEntered] = useState(false);
|
||||
const featureRefDesktop = useRef<HTMLDivElement>(null);
|
||||
const featureRefMobile = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleOnViewportFeatureEnter = () => {
|
||||
if (viewPortEntered) return;
|
||||
|
||||
setViewPortEntered(true);
|
||||
|
||||
if (!featureRefDesktop.current || !featureRefMobile.current) return;
|
||||
const videoDesktopRefs = featureRefDesktop.current.children;
|
||||
const videoMobileRefs = featureRefMobile.current.children;
|
||||
|
||||
if (!videoDesktopRefs || !videoMobileRefs) return;
|
||||
|
||||
for (let i = 0; i < videoDesktopRefs.length; i++) {
|
||||
const videoMobileEl = videoDesktopRefs[i] as HTMLVideoElement;
|
||||
const videoDesktopEl = videoMobileRefs[i] as HTMLVideoElement;
|
||||
|
||||
if (videoMobileEl.dataset.src && videoDesktopEl.dataset.src) {
|
||||
videoDesktopEl.src = videoDesktopEl.dataset.src;
|
||||
videoMobileEl.src = videoMobileEl.dataset.src;
|
||||
}
|
||||
}
|
||||
|
||||
setViewPortEntered(true);
|
||||
};
|
||||
|
||||
async function getProjects() {
|
||||
try {
|
||||
@@ -336,10 +373,8 @@ export default function App() {
|
||||
|
||||
<motion.div
|
||||
className="xl:grid flex flex-col grid-cols-2 xl:gap-4 sm:gap-10 gap-8 2xl:mb-[200px] sm:mb-[120px] mb-20"
|
||||
viewport={{ once: true, margin: "-10%" }}
|
||||
onViewportEnter={() =>
|
||||
console.log("first", featureRef.current.firstElementChild)
|
||||
}
|
||||
viewport={{ margin: "-10%" }}
|
||||
onViewportEnter={handleOnViewportFeatureEnter}
|
||||
>
|
||||
<div className="flex flex-col gap-20">
|
||||
<div className="xl:flex flex-col grid sm:grid-cols-2 xl:gap-8 gap-3">
|
||||
@@ -416,14 +451,13 @@ export default function App() {
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="aspect-video sticky top-[25%] xl:block hidden"
|
||||
ref={featureRef}
|
||||
>
|
||||
<FeatureVideoViewBox video={selectedVideo} />
|
||||
</div>
|
||||
<FeatureVideoViewBox
|
||||
selectedVideo={selectedVideo}
|
||||
videos={VIDEOS}
|
||||
featureRef={featureRefDesktop}
|
||||
/>
|
||||
|
||||
<VideoSliderMobile />
|
||||
<VideoSliderMobile featureRef={featureRefMobile} />
|
||||
</motion.div>
|
||||
|
||||
{/* <Histories /> */}
|
||||
|
||||
@@ -1,36 +1,65 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
interface FeatureVideoViewBoxProps {
|
||||
video: string;
|
||||
selectedVideo: string;
|
||||
videos: string[];
|
||||
featureRef: React.LegacyRef<HTMLDivElement>;
|
||||
}
|
||||
|
||||
function FeatureVideoViewBox({ video }: FeatureVideoViewBoxProps) {
|
||||
function FeatureVideoViewBox({
|
||||
selectedVideo,
|
||||
videos,
|
||||
featureRef,
|
||||
}: FeatureVideoViewBoxProps) {
|
||||
// const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const videoContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!selectedVideo || !videoContainerRef.current) return;
|
||||
|
||||
// const videoEl = document.createElement("video");
|
||||
// videoEl.src = selectedVideo;
|
||||
// videoEl.muted = true;
|
||||
// videoEl.autoplay = true;
|
||||
// videoEl.loop = true;
|
||||
// videoEl.playsInline = true;
|
||||
// videoEl.preload = "metadata";
|
||||
// videoEl.classList.add("absolute", "h-fit");
|
||||
// // videoEl.onloadeddata = () => console.log("onloadeddata");
|
||||
|
||||
// videoContainerRef.current.appendChild(videoEl);
|
||||
|
||||
// if (videoContainerRef.current.childElementCount > 1) {
|
||||
// setTimeout(() => {
|
||||
// videoContainerRef.current?.firstElementChild?.remove();
|
||||
// }, 5000);
|
||||
// }
|
||||
// }, [selectedVideo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!video || !videoContainerRef.current) return;
|
||||
return () => {};
|
||||
}, [selectedVideo]);
|
||||
|
||||
const videoEl = document.createElement("video");
|
||||
videoEl.src = video;
|
||||
videoEl.muted = true;
|
||||
videoEl.autoplay = true;
|
||||
videoEl.loop = true;
|
||||
videoEl.playsInline = true;
|
||||
videoEl.preload = "metadata";
|
||||
videoEl.classList.add("absolute", "h-fit");
|
||||
// videoEl.onloadeddata = () => console.log("onloadeddata");
|
||||
|
||||
videoContainerRef.current.appendChild(videoEl);
|
||||
|
||||
if (videoContainerRef.current.childElementCount > 1) {
|
||||
setTimeout(() => {
|
||||
videoContainerRef.current?.firstElementChild?.remove();
|
||||
}, 5000);
|
||||
}
|
||||
}, [video]);
|
||||
|
||||
return <div ref={videoContainerRef} className="relative h-fit"></div>;
|
||||
return (
|
||||
<div className="aspect-video sticky top-[25%] xl:block hidden">
|
||||
<div ref={featureRef} className="relative h-fit">
|
||||
{videos.map((video) => (
|
||||
<video
|
||||
data-src={video}
|
||||
key={video}
|
||||
muted
|
||||
autoPlay
|
||||
loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
className={`absolute h-fit ${
|
||||
selectedVideo === video ? "block" : "hidden"
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FeatureVideoViewBox;
|
||||
|
||||
@@ -6,7 +6,11 @@ import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
|
||||
function VideoSliderMobile() {
|
||||
interface IVideoSliderMobile {
|
||||
featureRef: React.LegacyRef<HTMLDivElement>;
|
||||
}
|
||||
|
||||
function VideoSliderMobile({ featureRef }: IVideoSliderMobile) {
|
||||
const [items] = useState<any[]>([
|
||||
{
|
||||
title: "Виртуальный тур по жилому комплексу",
|
||||
@@ -102,7 +106,7 @@ function VideoSliderMobile() {
|
||||
className="xl:hidden flex flex-col sm:gap-6 gap-4 border-b border-[#3D425C] pb-5"
|
||||
>
|
||||
<div
|
||||
// ref={videosContainerRef}
|
||||
ref={featureRef}
|
||||
className={`relative flex sm:gap-[88px] items-start transition-all duration-500`}
|
||||
style={{ left: `-${activeIndex * 100}%` }}
|
||||
>
|
||||
@@ -110,7 +114,7 @@ function VideoSliderMobile() {
|
||||
<motion.video
|
||||
key={index}
|
||||
ref={videoRefs[index]}
|
||||
src={item.video}
|
||||
data-src={item.video}
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
|
||||
Reference in New Issue
Block a user