From 939e9f435c7fb2b83b83a213db70d9a4a3ff8dc6 Mon Sep 17 00:00:00 2001 From: Lanskikh Date: Fri, 7 Mar 2025 19:43:46 +0500 Subject: [PATCH] upgrade usability desktop presentation --- .../Presentation/PresentationDesktop.tsx | 71 ++---- .../DesktopPresentation/AvatarCard.tsx | 3 +- .../DesktopPresentation/InteractiveWindow.tsx | 3 +- .../pages/PrimePage/PrimeAnimations.tsx | 220 +++++++++--------- src/components/pages/PrimePage/PrimePage.tsx | 79 +++++-- src/components/slides/Engine.tsx | 2 +- src/components/slides/Infrastructure.tsx | 4 +- src/components/slides/Insolation.tsx | 4 +- src/components/slides/IntegrationCRM.tsx | 2 +- src/components/slides/SearchAndSelect.tsx | 2 +- src/components/slides/ThreeDTour.tsx | 2 +- src/components/slides/VideoLayerMain.tsx | 2 +- src/lib/Providers.tsx | 12 +- src/ui/PrimeProgressItem.tsx | 14 +- 14 files changed, 215 insertions(+), 205 deletions(-) diff --git a/src/components/pages/MainPage/Presentation/PresentationDesktop.tsx b/src/components/pages/MainPage/Presentation/PresentationDesktop.tsx index 39d3ad21..4c9dad4b 100644 --- a/src/components/pages/MainPage/Presentation/PresentationDesktop.tsx +++ b/src/components/pages/MainPage/Presentation/PresentationDesktop.tsx @@ -1,10 +1,8 @@ 'use client'; -import Lenis from 'lenis'; -import ReactLenis, { useLenis } from 'lenis/react'; import { videos } from '@/consts/presentation/videos'; import { Title } from '@/ui/Title'; -import { useInView, useMotionValueEvent, useScroll } from 'framer-motion'; +import { useMotionValueEvent, useScroll } from 'framer-motion'; import { useRef, useState } from 'react'; import { Engine } from '../../../slides/Engine'; import { Infrastructure } from '../../../slides/Infrastructure'; @@ -16,24 +14,15 @@ import { VideoLayerMain } from '../../../slides/VideoLayerMain'; import { PrimeProgressItem } from '@/ui/PrimeProgressItem'; export function PresentationDesktop() { - const container = useRef(null); + const target = useRef(null); - const { scrollYProgress } = useScroll({ container }); - - const inView = useInView(container, { margin: '80% 0% -80% 0%' }); + const { scrollYProgress } = useScroll({ target }); const [slide, setSlide] = useState(0); - const [completed, setCompleted] = useState(false); - - const lenis = useLenis(({}) => { - console.log('asd'); - }); - - useMotionValueEvent(scrollYProgress, 'change', (value) => { - setCompleted(value >= 0.99); - setSlide(Math.min(Math.trunc(value * videos.length), videos.length - 1)); - }); + useMotionValueEvent(scrollYProgress, 'change', (value) => + setSlide(Math.min(Math.trunc(value * videos.length), videos.length - 1)) + ); return (
@@ -42,18 +31,8 @@ export function PresentationDesktop() { улучшает опыт выбора недвижимости{' '} и увеличивает темпы продаж квартир в жилом комплексе -
-
- {Array.from({ length: videos.length }).map((_, index) => ( -
- ))} -
- +
+
@@ -61,23 +40,23 @@ export function PresentationDesktop() { - -
-
- {videos.map(({ src, anchorImg }, index) => ( - { - container.current?.scrollTo({ - top: (index * container.current?.scrollHeight) / videos.length, - behavior: slide === videos.length - 1 ? 'auto' : 'smooth', - }); - }} - active={index === slide} - src={anchorImg} - title={src} - key={src} - /> - ))} +
+ {videos.map(({ src, anchorImg }, index) => ( + {}} + active={index === slide} + src={anchorImg} + title={src} + key={src} + /> + ))} +
+
+
+ {Array.from({ length: videos.length + 1 }).map((_, index) => ( +
+ ))} +
); diff --git a/src/components/pages/PrimePage/DesktopPresentation/AvatarCard.tsx b/src/components/pages/PrimePage/DesktopPresentation/AvatarCard.tsx index bc5a7da7..127ecd97 100644 --- a/src/components/pages/PrimePage/DesktopPresentation/AvatarCard.tsx +++ b/src/components/pages/PrimePage/DesktopPresentation/AvatarCard.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-img-element */ import { motion } from 'framer-motion'; export function AvatarCard({ slide }: { slide: number }) { @@ -10,7 +11,7 @@ export function AvatarCard({ slide }: { slide: number }) { className="p-[1.389vw] rounded-[1.389vw] flex flex-col justify-between items-center absolute bg-[#37393B99] w-[8.75vw] h-[10.625vw] right-[16.111vw]" > -

Аватар клиента

+

Аватар клиента

); } diff --git a/src/components/pages/PrimePage/DesktopPresentation/InteractiveWindow.tsx b/src/components/pages/PrimePage/DesktopPresentation/InteractiveWindow.tsx index 0f0fcab0..db9dfb81 100644 --- a/src/components/pages/PrimePage/DesktopPresentation/InteractiveWindow.tsx +++ b/src/components/pages/PrimePage/DesktopPresentation/InteractiveWindow.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-img-element */ import { motion } from 'framer-motion'; export function InteractiveWindowCard({ slide }: { slide: number }) { @@ -14,7 +15,7 @@ export function InteractiveWindowCard({ slide }: { slide: number }) { className="absolute left-[2.014vw] bottom-0 h-full" alt="" /> -

Интерактивное окно

+

Интерактивное окно

); } diff --git a/src/components/pages/PrimePage/PrimeAnimations.tsx b/src/components/pages/PrimePage/PrimeAnimations.tsx index afded571..a0ec9a2e 100644 --- a/src/components/pages/PrimePage/PrimeAnimations.tsx +++ b/src/components/pages/PrimePage/PrimeAnimations.tsx @@ -27,17 +27,15 @@ import { AvatarCard } from './DesktopPresentation/AvatarCard'; import { ScenarioCard } from './DesktopPresentation/ScenarioCard'; export function PrimeAnimations({ - primePresentationContainer, primePresentationSlide, }: { - primePresentationContainer: RefObject; primePresentationSlide: number; }) { const [slide, setSlide] = useState(0); - const container = useRef(null); + const target = useRef(null); - const { scrollYProgress } = useScroll({ container }); + const { scrollYProgress } = useScroll({ target }); const slidesCount = 9; @@ -45,122 +43,112 @@ export function PrimeAnimations({ setSlide(Math.min(Math.trunc(value * slidesCount), slidesCount - 1)) ); - const inView = useInView(container, { margin: '60% 0% -60% 0%' }); + const inView = useInView(target, { margin: '60% 0% -60% 0%' }); return ( -
- - - - - - - - - - - - - - - - - - 0 - ? { - bottom: slide > 2 ? '2.986vw' : '20vw', - width: '13.958vw', - height: '12.014vw', - scale: slide > 1 ? 0.83 : 1, - } - : { bottom: '10.972vw', width: '15.417vw', height: '13.194vw' } - } - transition={{ bounce: 'none' }} - src="/icons/folderBack.svg" - className="left-1/2 absolute -translate-x-1/2" - /> - 1 - ? { - backgroundColor: 'transparent', - scale: 0.83, - bottom: slide > 2 ? '12.889vw' : '29.722vw', - } - : slide > 0 - ? { - backgroundColor: 'transparent', - y: '14.722vw', - } - : { - backgroundColor: '#37393B99', - backdropFilter: 'blur(20px)', - y: inView ? '14.722vw' : '-50%', - } - } - transition={{ bounce: 'none' }} - className="flex absolute p-[0.556vw] z-10 rounded-[1.875vw] bg-[#37393B99] left-1/2 -translate-x-1/2" - > - {primeVideos.map(({ src, anchorImg }, index) => ( - 0} - {...primeProgressItemsTranslates[index]} - onClick={() => { - primePresentationContainer.current?.scrollTo({ - top: - (index * primePresentationContainer.current?.scrollHeight) / - primeVideos.length, - behavior: 'smooth', - }); - }} - active={index === primePresentationSlide} - src={anchorImg} - title={src} - key={src} - /> - ))} - - 0 - ? { - bottom: slide > 2 ? '2.986vw' : '20vw', - width: '15.833vw', - height: '9.792vw', - scale: slide > 1 ? 0.83 : 1, - } - : { width: '17.361vw', height: '10.764vw', bottom: '10.972vw' } - } - transition={{ bounce: 'none' }} - src="/icons/folderFront.svg" - className="left-1/2 absolute z-10 -translate-x-1/2" - /> - 2 - ? { bottom: 0 } - : { opacity: +(slide > 1), bottom: '17.014vw' } - } - transition={{ bounce: 'none' }} - className="rounded-[1.389vw] border border-[#37393B] aspect-square w-[15.972vw] absolute left-1/2 -translate-x-1/2 p-[1.111vw] flex items-end" - > - 1) }} +
+
+ + + + + + + + + + + + + + + + + + 0 + ? { + bottom: slide > 2 ? '2.986vw' : '20vw', + width: '13.958vw', + height: '12.014vw', + scale: slide > 1 ? 0.83 : 1, + } + : { bottom: '10.972vw', width: '15.417vw', height: '13.194vw' } + } + transition={{ bounce: 'none' }} + src="/icons/folderBack.svg" + className="left-1/2 absolute -translate-x-1/2" + /> + {/* 1 + ? { + backgroundColor: 'transparent', + scale: 0.83, + bottom: slide > 2 ? '12.889vw' : '29.722vw', + } + : slide > 0 + ? { + backgroundColor: 'transparent', + y: '14.722vw', + } + : { + backgroundColor: '#37393B99', + backdropFilter: 'blur(20px)', + y: inView ? '14.722vw' : '-50%', + } + } + transition={{ bounce: 'none' }} + className="flex absolute p-[0.556vw] z-10 rounded-[1.875vw] bg-[#37393B99] left-1/2 -translate-x-1/2" > - Базовый функционал - - -
+ {primeVideos.map(({ src, anchorImg }, index) => ( + 0} + {...primeProgressItemsTranslates[index]} + onClick={() => {}} + active={index === primePresentationSlide} + src={anchorImg} + title={src} + key={src} + /> + ))} + */} + 0 + ? { + bottom: slide > 2 ? '2.986vw' : '20vw', + width: '15.833vw', + height: '9.792vw', + scale: slide > 1 ? 0.83 : 1, + } + : { width: '17.361vw', height: '10.764vw', bottom: '10.972vw' } + } + transition={{ bounce: 'none' }} + src="/icons/folderFront.svg" + className="left-1/2 absolute z-10 -translate-x-1/2" + /> + 2 + ? { bottom: 0 } + : { opacity: +(slide > 1), bottom: '17.014vw' } + } + transition={{ bounce: 'none' }} + className="rounded-[1.389vw] border border-[#37393B] aspect-square w-[15.972vw] absolute left-1/2 -translate-x-1/2 p-[1.111vw] flex items-end" + > + 1) }} + > + Базовый функционал + + +
+
{Array.from({ length: 9 }).map((_, index) => ( -
+
))}
diff --git a/src/components/pages/PrimePage/PrimePage.tsx b/src/components/pages/PrimePage/PrimePage.tsx index e6fa12c3..357e1b46 100644 --- a/src/components/pages/PrimePage/PrimePage.tsx +++ b/src/components/pages/PrimePage/PrimePage.tsx @@ -3,7 +3,12 @@ import { primeVideos } from '@/consts/presentation/videos'; import { Title } from '@/ui/Title'; -import { useInView, useMotionValueEvent, useScroll } from 'framer-motion'; +import { + motion, + useInView, + useMotionValueEvent, + useScroll, +} from 'framer-motion'; import { useRef, useState } from 'react'; import { CommercialOffer } from '../../slides/CommercialOffer'; import { Favorites } from '../../slides/Favorites'; @@ -14,22 +19,24 @@ import { ThreeDTour } from '../../slides/ThreeDTour'; import { VideoLayerPrime } from '../../slides/VideoLayerPrime'; import { Insolation } from '../../slides/Insolation'; import { PrimeAnimations } from './PrimeAnimations'; +import { PrimeProgressItem } from '@/ui/PrimeProgressItem'; +import { primeProgressItemsTranslates } from '@/consts/primeProgressItemsTranslates'; export function PrimeDesktopPage() { - const container = useRef(null); + const target = useRef(null); - const { scrollYProgress } = useScroll({ container }); + const { scrollYProgress } = useScroll({ target }); - const inView = useInView(container, { margin: '75% 0% -75% 0%' }); - - const [presentationSlide, setPresentationSlide] = useState(0); + const [primePresentationSlide, setPrimePresentationSlide] = useState(0); useMotionValueEvent(scrollYProgress, 'change', (value) => - setPresentationSlide( + setPrimePresentationSlide( Math.min(Math.trunc(value * primeVideos.length), primeVideos.length - 1) ) ); + const [slide, setSlide] = useState(0); + return (
@@ -37,18 +44,8 @@ export function PrimeDesktopPage() { <span className="text-gradient">опыт выбора недвижимости</span> и увеличивает темпы продаж квартир в жилом комплексе -
-
- {Array.from({ length: primeVideos.length }).map((_, index) => ( -
- ))} -
-
+
+
@@ -57,12 +54,48 @@ export function PrimeDesktopPage() { + 1 + // ? { + // backgroundColor: 'transparent', + // scale: 0.83, + // bottom: slide > 2 ? '12.889vw' : '29.722vw', + // } + // : slide > 0 + // ? { + // backgroundColor: 'transparent', + // y: '14.722vw', + // } + // : { + // backgroundColor: '#37393B99', + // backdropFilter: 'blur(20px)', + // y: inView ? '14.722vw' : '-50%', + // } + // } + // transition={{ bounce: 'none' }} + className="flex absolute bottom-0 p-[0.556vw] z-10 rounded-[1.875vw] bg-[#37393B99] left-1/2 -translate-x-1/2 translate-y-1/2" + > + {primeVideos.map(({ src, anchorImg }, index) => ( + 0} + // {...primeProgressItemsTranslates[index]} + onClick={() => {}} + active={index === primePresentationSlide} + src={anchorImg} + title={src} + key={src} + /> + ))} + +
+
+ {Array.from({ length: primeVideos.length + 1 }).map((_, index) => ( +
+ ))}
- +
); } diff --git a/src/components/slides/Engine.tsx b/src/components/slides/Engine.tsx index 1cb5efab..4a2928d8 100644 --- a/src/components/slides/Engine.tsx +++ b/src/components/slides/Engine.tsx @@ -9,7 +9,7 @@ export function Engine({ scroll: MotionValue; top?: number; }) { - const opacity = useTransform(scroll, [3 / 5, 4 / 5, 5 / 5], [0, 1, 0]); + const opacity = useTransform(scroll, [3 / 5, 4 / 5, 4.5 / 5], [0, 1, 0]); const x = useTransform(scroll, [3 / 5, 4 / 5], ['-100%', '0%']); diff --git a/src/components/slides/Infrastructure.tsx b/src/components/slides/Infrastructure.tsx index 793109a2..416750a1 100644 --- a/src/components/slides/Infrastructure.tsx +++ b/src/components/slides/Infrastructure.tsx @@ -26,7 +26,7 @@ export function Infrastructure({ const opacityMain = useTransform( scrollProgress, - [1 / 5, 2 / 5, 3 / 5], + [1 / 5, 2 / 5, 1 / 2], [0, 1, 0] ); @@ -45,7 +45,7 @@ export function Infrastructure({ x: page === 'main' ? xMain : xPrime, opacity: page === 'main' ? opacityMain : opacityPrime, }} - className={`absolute max-lg:hidden right-0 rounded-[1.111vw] p-[1.667vw] h-full bg-radial-[at_0%_100%] from-[#7A7A7A99] flex flex-col gap-2 backdrop-blur-[500px] select-none ${ + className={`absolute max-lg:hidden right-[1.389vw] rounded-[1.111vw] p-[1.667vw] h-full bg-radial-[at_0%_100%] from-[#7A7A7A99] flex flex-col gap-2 backdrop-blur-[500px] select-none ${ page === 'main' ? 'w-[31.944vw]' : 'w-[23.611vw]' }`} > diff --git a/src/components/slides/Insolation.tsx b/src/components/slides/Insolation.tsx index 20e9456c..f222a845 100644 --- a/src/components/slides/Insolation.tsx +++ b/src/components/slides/Insolation.tsx @@ -13,6 +13,8 @@ export function Insolation({ }) { const y = useTransform(scrollProgress, [2 / 5, 3 / 5], ['100%', '0%']); + const xMain = useTransform(scrollProgress, [3 / 5, 4 / 5], ['0%', '100%']); + const x = useTransform( scrollProgress, [4 / 6, 5 / 6, 6 / 6], @@ -39,7 +41,7 @@ export function Insolation({ style={{ y: page === 'main' ? y : undefined, opacity: page === 'main' ? opacityMain : opacityPrime, - x: page === 'prime' ? x : undefined, + x: page === 'prime' ? x : xMain, }} className={`absolute max-lg:hidden h-full rounded-[1.111vw] p-[1.667vw] bg-radial-[at_0%_100%] from-[#7A7A7A99] flex flex-col gap-2 backdrop-blur-[500px] select-none ${ page === 'main' ? 'w-[31.944vw] right-0' : 'w-[23.611vw]' diff --git a/src/components/slides/IntegrationCRM.tsx b/src/components/slides/IntegrationCRM.tsx index 01d20eaa..386e187b 100644 --- a/src/components/slides/IntegrationCRM.tsx +++ b/src/components/slides/IntegrationCRM.tsx @@ -37,7 +37,7 @@ export function IntegrationCRM({ opacity: page === 'main' ? opacityMain : opacityPrime, }} className={`absolute hidden h-full p-[1.667vw] rounded-[1.111vw] bg-radial-[at_100%_100%] from-[#7A7A7A66] backdrop-blur-[500px] bottom-0 to-transparent lg:flex flex-col justify-between select-none ${ - page === 'main' ? 'w-[29.167vw]' : 'w-[23.611vw] right-0' + page === 'main' ? 'w-[29.167vw]' : 'w-[23.611vw] right-[1.389vw]' }`} >

diff --git a/src/components/slides/SearchAndSelect.tsx b/src/components/slides/SearchAndSelect.tsx index 7a666a6f..a97769ed 100644 --- a/src/components/slides/SearchAndSelect.tsx +++ b/src/components/slides/SearchAndSelect.tsx @@ -16,7 +16,7 @@ export function SearchAndSelect({ }) { const opacityMain = useTransform( scrollProgress, - [0, 1 / (videos.length - 1)], + [0, 1 / (videos.length - 1) / 2], [1, 0] ); diff --git a/src/components/slides/ThreeDTour.tsx b/src/components/slides/ThreeDTour.tsx index e07cc252..fbca9900 100644 --- a/src/components/slides/ThreeDTour.tsx +++ b/src/components/slides/ThreeDTour.tsx @@ -58,7 +58,7 @@ export function ThreeDTour({ x: page === 'main' ? xMain : xPrime, }} className={`p-[1.667vw] max-lg:hidden rounded-[1.111vw] bg-radial-[at_100%_100%] from-[#7A7A7A66] backdrop-blur-[500px] aspect-[340/368] flex flex-col justify-between gap-[4.444vw] absolute w-[23.611vw] ${ - page === 'prime' ? ' right-0 top-1/2 -translate-y-1/2' : '' + page === 'prime' ? ' right-[1.389vw] top-1/2 -translate-y-1/2' : '' }`} >

diff --git a/src/components/slides/VideoLayerMain.tsx b/src/components/slides/VideoLayerMain.tsx index 1acdf785..9fd2f61d 100644 --- a/src/components/slides/VideoLayerMain.tsx +++ b/src/components/slides/VideoLayerMain.tsx @@ -74,7 +74,7 @@ export function VideoLayerMain({ scroll }: { scroll: MotionValue }) { loop muted playsInline - animate={{ y: slide === videos.length - 1 ? '7.292vw' : '0vws' }} + animate={{ y: slide === videos.length - 1 ? '7.292vw' : '0vw' }} transition={{ duration: 0.1 }} style={{ zIndex: videos.length - index }} className={`absolute w-full h-full duration-500 transition-[opacity,transform]${ diff --git a/src/lib/Providers.tsx b/src/lib/Providers.tsx index 96f04121..d4b3abb6 100644 --- a/src/lib/Providers.tsx +++ b/src/lib/Providers.tsx @@ -7,14 +7,14 @@ import { useState } from 'react'; export const Providers = ({ children }: { children: React.ReactNode }) => { const [queryClient] = useState(() => new QueryClient()); - // const lenis = new Lenis(); + const lenis = new Lenis(); - // function raf(time: number) { - // lenis.raf(time); - // requestAnimationFrame(raf); - // } + function raf(time: number) { + lenis.raf(time); + requestAnimationFrame(raf); + } - // requestAnimationFrame(raf); + requestAnimationFrame(raf); return ( diff --git a/src/ui/PrimeProgressItem.tsx b/src/ui/PrimeProgressItem.tsx index e2c7c37e..ccd790ad 100644 --- a/src/ui/PrimeProgressItem.tsx +++ b/src/ui/PrimeProgressItem.tsx @@ -26,8 +26,7 @@ export function PrimeProgressItem({