fixed and changed navigation buttons, changed slides animations

This commit is contained in:
2025-03-20 18:21:25 +05:00
parent 173c62d57c
commit a628ff86fd
17 changed files with 324 additions and 129 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

@@ -2,7 +2,7 @@
import { videos } from '@/consts/presentation/videos';
import { Title } from '@/ui/Title';
import { useMotionValueEvent, useScroll, scroll } from 'framer-motion';
import { useMotionValueEvent, useScroll } from 'framer-motion';
import { useRef, useState } from 'react';
import { Engine } from '../../../slides/Engine';
import { Infrastructure } from '../../../slides/Infrastructure';
@@ -21,9 +21,9 @@ export function PresentationDesktop() {
const [slide, setSlide] = useState(0);
useMotionValueEvent(scrollYProgress, 'change', (value) => {
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 (
<div className="mt-25 mb-60 max-lg:hidden relative">
@@ -32,7 +32,7 @@ export function PresentationDesktop() {
<span className="text-gradient">улучшает опыт выбора недвижимости</span>{' '}
и&nbsp;увеличивает темпы продаж квартир в&nbsp;жилом комплексе
</Title>
<div className="relative h-[272.223vw]f h-[233.334vw]" ref={container}>
<div className="relative h-[233.334vw]" ref={container}>
<div className="top-30 w-full h-[38.889vw] sticky">
<VideoLayerMain scroll={scrollYProgress} />
<SearchAndSelect scrollProgress={scrollYProgress} page="main" />
@@ -42,7 +42,7 @@ export function PresentationDesktop() {
<Engine scroll={scrollYProgress} />
<IntegrationCRM scrollProgress={scrollYProgress} page="main" />
<div className="flex absolute bottom-0 p-[0.556vw] rounded-[1.875vw] bg-[#37393B99] backdrop-blur-[20px] left-1/2 -translate-x-1/2 translate-y-1/2 z-50">
{videos.map(({ src, anchorImg }, index) => (
{videos.map(({ src, anchorImg, title }, index) => (
<PrimeProgressItem
onClick={() => {
if (!target.current) return;
@@ -50,22 +50,19 @@ export function PresentationDesktop() {
top:
target.current.getBoundingClientRect().top -
document.body.getBoundingClientRect().top +
(index / 6) * target.current.scrollHeight,
(index / 6) * target.current.clientHeight -
120,
});
}}
active={index === slide}
src={anchorImg}
title={src}
title={title}
key={src}
/>
))}
</div>
</div>
<div ref={target} className="h-full top-0 w-1/10 absolute">
{Array.from({ length: videos.length }).map((_, index) => (
<div key={index} className="w-full h-1/6" />
))}
</div>
<div ref={target} className="h-full absolute top-0" />
</div>
</div>
);
@@ -14,38 +14,38 @@ export function FolderAnimation({ scroll }: { scroll: MotionValue<number> }) {
setSlide(
Math.min(
Math.trunc(value * (primeVideos.length + 9)),
primeVideos.length + 8
primeVideos.length + 7
)
)
);
const y = useTransform(
scroll,
[6 / (primeVideos.length + 8), 7 / (primeVideos.length + 8)],
[6 / (primeVideos.length + 7), 7 / (primeVideos.length + 7)],
['49.444vw', '0vw']
);
const scale = useTransform(
scroll,
[8 / (primeVideos.length + 8), 9 / (primeVideos.length + 8)],
[8 / (primeVideos.length + 7), 9 / (primeVideos.length + 7)],
[1, 0.83]
);
const bottomBorder = useTransform(
scroll,
[9 / (primeVideos.length + 8), 10 / (primeVideos.length + 8)],
[9 / (primeVideos.length + 7), 10 / (primeVideos.length + 7)],
['17.014vw', '0vw']
);
const bottomFolder = useTransform(
scroll,
[9 / (primeVideos.length + 8), 10 / (primeVideos.length + 8)],
[9 / (primeVideos.length + 7), 10 / (primeVideos.length + 7)],
['19.931vw', '2.986vw']
);
const opacity = useTransform(
scroll,
[9 / (primeVideos.length + 8), 10 / (primeVideos.length + 8)],
[9 / (primeVideos.length + 7), 10 / (primeVideos.length + 7)],
[0, 1]
);
@@ -9,14 +9,20 @@ import {
} from 'framer-motion';
import { useState } from 'react';
export function PrimeProgress({ scroll }: { scroll: MotionValue<number> }) {
export function PrimeProgress({
scroll,
target,
}: {
scroll: MotionValue<number>;
target: HTMLDivElement;
}) {
const [slide, setSlide] = useState(0);
useMotionValueEvent(scroll, 'change', (value) =>
setSlide(
Math.min(
Math.trunc(value * (primeVideos.length + 9)),
primeVideos.length + 8
primeVideos.length + 7
)
)
);
@@ -24,18 +30,18 @@ export function PrimeProgress({ scroll }: { scroll: MotionValue<number> }) {
const bottom = useTransform(
scroll,
[
6 / (primeVideos.length + 8),
7 / (primeVideos.length + 8),
8 / (primeVideos.length + 8),
9 / (primeVideos.length + 8),
10 / (primeVideos.length + 8),
6 / (primeVideos.length + 7),
7 / (primeVideos.length + 7),
8 / (primeVideos.length + 7),
9 / (primeVideos.length + 7),
10 / (primeVideos.length + 7),
],
['0vw', '31.389vw', '31.389vw', '28.794vw', '12.194vw']
);
const scale = useTransform(
scroll,
[8 / (primeVideos.length + 8), 9 / (primeVideos.length + 8)],
[8 / (primeVideos.length + 7), 9 / (primeVideos.length + 7)],
[1, 0.83]
);
@@ -49,17 +55,21 @@ export function PrimeProgress({ scroll }: { scroll: MotionValue<number> }) {
transition={{ bounce: false }}
className="flex absolute p-[0.556vw] z-10 rounded-[1.875vw] bg-[#37393B99] left-1/2 -translate-1/2 transition-colors"
>
{primeVideos.map(({ src, anchorImg }, index) => (
{primeVideos.map(({ src, anchorImg, title }, index) => (
<PrimeProgressItem
canAnimate={slide > 7}
{...(slide > 7 ? primeProgressItemsTranslates[index] : undefined)}
onClick={() => {
window.scrollTo({ top: 0 });
console.log('as');
window.scroll({
top:
target.getBoundingClientRect().top -
document.body.getBoundingClientRect().top +
(index / 15) * target.clientHeight,
});
}}
active={index === slide}
src={anchorImg}
title={src}
title={title}
key={src}
/>
))}
+8 -11
View File
@@ -3,8 +3,8 @@
import { primeVideos } from '@/consts/presentation/videos';
import { Title } from '@/ui/Title';
import { motion, useMotionValueEvent, useScroll } from 'framer-motion';
import { useEffect, useRef, useState } from 'react';
import { useMotionValueEvent, useScroll } from 'framer-motion';
import { useRef, useState } from 'react';
import { CommercialOffer } from '../../slides/CommercialOffer';
import { Favorites } from '../../slides/Favorites';
import { Infrastructure } from '../../slides/Infrastructure';
@@ -13,8 +13,6 @@ import { SearchAndSelect } from '../../slides/SearchAndSelect';
import { ThreeDTour } from '../../slides/ThreeDTour';
import { VideoLayerPrime } from '../../slides/VideoLayerPrime';
import { Insolation } from '../../slides/Insolation';
import { PrimeProgressItem } from '@/ui/PrimeProgressItem';
import { PrimeAnimations } from './PrimeAnimations';
import { PackageTitle } from './DesktopPresentation/PackageTitle';
import { DesignCard } from './DesktopPresentation/DesignCard';
import { EnvironmentCard } from './DesktopPresentation/EnvironmentCard';
@@ -44,7 +42,7 @@ export function PrimeDesktopPage() {
setSlide(
Math.min(
Math.trunc(value * (primeVideos.length + 9)),
primeVideos.length + 8
primeVideos.length + 7
)
)
);
@@ -52,14 +50,14 @@ export function PrimeDesktopPage() {
const [slide, setSlide] = useState(0);
return (
<div className="max-lg:hidden space-y-[1.667vw]">
<div className="max-lg:hidden space-y-[3.333vw]">
<Title headerLevel={2}>
Интерактивная презентация улучшает{' '}
<span className="text-gradient">опыт выбора недвижимости</span> и
увеличивает темпы продаж квартир в&nbsp;жилом комплексе
</Title>
<div className="relative h-[1498.368vh]">
<div className="h-[93.684vh] sticky top-[1.389vw]m top-[1.667vw] -mx-[1.389vw] px-[1.389vw]">
<div className="h-[93.684vh] sticky top-[3.333vw] -mx-[1.389vw] px-[1.389vw]">
<VideoLayerPrime scroll={scrollYProgress} />
<ThreeDTour scrollProgress={scrollYProgress} page="prime" />
<SearchAndSelect scrollProgress={scrollYProgress} page="prime" />
@@ -68,9 +66,7 @@ export function PrimeDesktopPage() {
<IntegrationCRM scrollProgress={scrollYProgress} page="prime" />
<Insolation scrollProgress={scrollYProgress} page="prime" />
<CommercialOffer scroll={scrollYProgress} />
<PackageTitle slide={slide} />
<DesignCard slide={slide} />
<EnvironmentCard slide={slide} />
<ThreeDReelsCard slide={slide} />
@@ -87,9 +83,10 @@ export function PrimeDesktopPage() {
<InteractiveWindowCard slide={slide} />
<AvatarCard slide={slide} />
<ScenarioCard slide={slide} />
<FolderAnimation scroll={scrollYProgress} />
<PrimeProgress scroll={scrollYProgress} />
{target.current && (
<PrimeProgress target={target.current!} scroll={scrollYProgress} />
)}
</div>
<div ref={target} className="h-full" />
</div>
+4 -4
View File
@@ -5,19 +5,19 @@ import { motion, MotionValue, useTransform } from 'framer-motion';
export function CommercialOffer({ scroll }: { scroll: MotionValue<number> }) {
const x = useTransform(
scroll,
[5 / (primeVideos.length + 8), 6 / (primeVideos.length + 8)],
[5 / (primeVideos.length + 7), 6 / (primeVideos.length + 7)],
['100%', '0%']
);
const y = useTransform(
scroll,
[6 / (primeVideos.length + 8), 7 / (primeVideos.length + 8)],
['0%', '-110%']
[6 / (primeVideos.length + 7), 7 / (primeVideos.length + 7)],
['0%', '-100%']
);
const opacity = useTransform(
scroll,
[5 / (primeVideos.length + 8), 6 / (primeVideos.length + 8)],
[5 / (primeVideos.length + 7), 6 / (primeVideos.length + 7)],
[0, 1]
);
+14 -2
View File
@@ -10,9 +10,21 @@ export function Engine({
scroll: MotionValue<number>;
top?: number;
}) {
const opacity = useTransform(scroll, [3 / 5, 4 / 5, 4.5 / 5], [0, 1, 0]);
const opacity = useTransform(
scroll,
[
3 / (videos.length - 1),
4 / (videos.length - 1),
4.5 / (videos.length - 1),
],
[0, 1, 0]
);
const x = useTransform(scroll, [3 / 5, 4 / 5], ['-100%', '0%']);
const x = useTransform(
scroll,
[3 / (videos.length - 1), 4 / (videos.length - 1)],
['-100%', '0%']
);
const opacityMini = useTransform(
scroll,
+14 -11
View File
@@ -4,27 +4,30 @@ import { motion, MotionValue, useTransform } from 'framer-motion';
export function Favorites({ scroll }: { scroll: MotionValue<number> }) {
const x = useTransform(
scroll,
[
2 / (primeVideos.length + 8),
3 / (primeVideos.length + 8),
4 / (primeVideos.length + 8),
],
['-100%', '0%', '-100%']
[3 / (primeVideos.length + 7), 4 / (primeVideos.length + 7)],
['0%', '100%']
);
const y = useTransform(
scroll,
[2 / (primeVideos.length + 7), 3 / (primeVideos.length + 7)],
['100%', '0%']
);
const opacity = useTransform(
scroll,
[
2 / (primeVideos.length + 8),
3 / (primeVideos.length + 8),
4 / (primeVideos.length + 8),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
3.5 / (primeVideos.length + 7),
],
[0, 1, 0]
);
return (
<motion.div
style={{ x, opacity }}
className="p-[1.667vw] backdrop-blur-xs rounded-2xl bg-[radial-gradient(ellipse_at_right,#7A7A7A66,transparent)] absolute h-[40.556vw] w-[23.611vw] flex flex-col justify-between gap-6"
style={{ x, opacity, y }}
className="right-[1.389vw] p-[1.667vw] backdrop-blur-xs rounded-2xl bg-[radial-gradient(ellipse_at_right,#7A7A7A66,transparent)] absolute h-[40.556vw] w-[23.611vw] flex flex-col justify-between gap-6"
>
<p className="heading2 font-medium">Формирование списка избранного</p>
<div className="rounded-full aspect-square w-[58.824%] self-center bg-[#D375FF] bg-[url(/img/pages/prime/favorite.jpg),url(/img/pages/prime/favorite.jpg),url(/img/pages/prime/favorite.jpg)] bg-no-repeat bg-[length:29%,35%,29%] bg-[position:-3.5%,center,right_-3.5%_center]" />
+11 -11
View File
@@ -17,30 +17,30 @@ export function Infrastructure({
top?: number;
page?: 'main' | 'prime';
}) {
const xMain = useTransform(scrollProgress, [1 / 5, 2 / 5], ['100%', '0%']);
const xMain = useTransform(
scrollProgress,
[1 / (videos.length - 1), 2 / (videos.length - 1)],
['100%', '0%']
);
const xPrime = useTransform(
scrollProgress,
[
1 / (primeVideos.length + 8),
2 / (primeVideos.length + 8),
3 / (primeVideos.length + 8),
],
['100%', '0%', '100%']
[1 / (primeVideos.length + 7), 2 / (primeVideos.length + 7)],
['100%', '0%']
);
const opacityMain = useTransform(
scrollProgress,
[1 / 5, 2 / 5, 1 / 2],
[1 / (videos.length - 1), 2 / (videos.length - 1), 1 / 2],
[0, 1, 0]
);
const opacityPrime = useTransform(
scrollProgress,
[
1 / (primeVideos.length + 8),
2 / (primeVideos.length + 8),
3 / (primeVideos.length + 8),
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
2.5 / (primeVideos.length + 7),
],
[0, 1, 0]
);
+28 -14
View File
@@ -12,32 +12,40 @@ export function Insolation({
top?: number;
page?: 'main' | 'prime';
}) {
const y = useTransform(scrollProgress, [2 / 5, 3 / 5], ['100%', '0%']);
const y = useTransform(
scrollProgress,
[2 / (videos.length - 1), 3 / (videos.length - 1)],
['100%', '0%']
);
const xMain = useTransform(scrollProgress, [3 / 5, 4 / 5], ['0%', '100%']);
const xMain = useTransform(
scrollProgress,
[3 / (videos.length - 1), 4 / (videos.length - 1)],
['0%', '100%']
);
const x = useTransform(
scrollProgress,
[
4 / (primeVideos.length + 8),
5 / (primeVideos.length + 8),
6 / (primeVideos.length + 8),
],
['-100%', '0%', '-100%']
[5 / (primeVideos.length + 7), 6 / (primeVideos.length + 7)],
['0%', '-100%']
);
const opacityMain = useTransform(
scrollProgress,
[2 / 5, 3 / 5, 3.5 / 5],
[
2 / (videos.length - 1),
3 / (videos.length - 1),
3.5 / (videos.length - 1),
],
[0, 1, 0]
);
const opacityPrime = useTransform(
scrollProgress,
[
4 / (primeVideos.length + 8),
5 / (primeVideos.length + 8),
6 / (primeVideos.length + 8),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
5.5 / (primeVideos.length + 7),
],
[0, 1, 0]
);
@@ -48,13 +56,19 @@ export function Insolation({
[1, 0]
);
const yPrime = useTransform(
scrollProgress,
[4 / (primeVideos.length + 7), 5 / (primeVideos.length + 7)],
['100%', '0%']
);
return (
<>
<motion.div
style={{
y: page === 'main' ? y : undefined,
y: page === 'main' ? y : yPrime,
opacity: page === 'main' ? opacityMain : opacityPrime,
x: page === 'prime' ? x : xMain,
x: page === 'main' ? xMain : x,
}}
className={`absolute max-lg:hidden rounded-[1.111vw] p-[1.667vw] [background:radial-gradient(ellipse_at_100%,#7A7A7A33,transparent)] flex flex-col gap-2 [backdrop-filter:blur(500px)] select-none ${
page === 'main'
+17 -13
View File
@@ -2,7 +2,7 @@
import { motion, MotionValue, useTransform } from 'framer-motion';
import MailIcon from '../../../public/icons/mail.svg';
import PhoneIcon from '../../../public/icons/phone.svg';
import { primeVideos } from '@/consts/presentation/videos';
import { primeVideos, videos } from '@/consts/presentation/videos';
export function IntegrationCRM({
scrollProgress,
@@ -13,28 +13,32 @@ export function IntegrationCRM({
top?: number;
page?: 'main' | 'prime';
}) {
const opacityMain = useTransform(scrollProgress, [4 / 5, 1], [0, 1]);
const opacityMain = useTransform(
scrollProgress,
[4 / (videos.length - 1), 1],
[0, 1]
);
const opacityPrime = useTransform(
scrollProgress,
[
3 / (primeVideos.length + 8),
4 / (primeVideos.length + 8),
5 / (primeVideos.length + 8),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
4.5 / (primeVideos.length + 7),
],
[0, 1, 0]
);
const y = useTransform(scrollProgress, [4 / 5, 1], ['100%', '0%']);
const y = useTransform(
scrollProgress,
[4 / (videos.length - 1), 1],
['100%', '0%']
);
const x = useTransform(
scrollProgress,
[
3 / (primeVideos.length + 8),
4 / (primeVideos.length + 8),
5 / (primeVideos.length + 8),
],
['100%', '0%', '100%']
[3 / (primeVideos.length + 7), 4 / (primeVideos.length + 7)],
['-100%', '0%']
);
return (
@@ -48,7 +52,7 @@ export function IntegrationCRM({
className={`absolute hidden p-[1.667vw] rounded-[1.111vw] [backdrop-filter:blur(500px)] [background:radial-gradient(ellipse_at_100%,#7A7A7A33,transparent)] lg:flex flex-col justify-between select-none ${
page === 'main'
? 'w-[29.167vw] h-full'
: 'w-[23.611vw] right-[1.389vw] h-[40.556vw]'
: 'w-[23.611vw] aright-[1.389vw] h-[40.556vw]'
}`}
>
<p className="heading2 font-medium">
+11 -4
View File
@@ -22,14 +22,20 @@ export function SearchAndSelect({
const opacityPrime = useTransform(
scrollProgress,
[0, 1 / (primeVideos.length + 8), 2 / (primeVideos.length + 8)],
[0, 1 / (primeVideos.length + 7), 1.5 / (primeVideos.length + 7)],
[0, 1, 0]
);
const x = useTransform(
scrollProgress,
[0, 1 / (primeVideos.length + 8), 2 / (primeVideos.length + 8)],
['-100%', '0%', '-100%']
[1 / (primeVideos.length + 7), 2 / (primeVideos.length + 7)],
['0%', '-100%']
);
const y = useTransform(
scrollProgress,
[0 / (primeVideos.length + 7), 1 / (primeVideos.length + 7)],
['100%', '0%']
);
const opacityMini = useTransform(
@@ -46,11 +52,12 @@ export function SearchAndSelect({
isLg
? {
opacity: page === 'main' ? opacityMain : opacityPrime,
y: page === 'prime' ? y : undefined,
x: page === 'prime' ? x : undefined,
}
: { top, opacity: opacityMini }
}
className={`max-md:p-5 flex max-md:flex-col lg:flex-col md:gap-3 max-md:gap-y-7 gap-y-3 max-md:rounded-2xl sticky max-md:h-full select-none max-md:[background:radial-gradient(ellipse_at_100%_100%,#7A7A7A33,transparent)] max-md:[backdrop-filter:blur(500px)] lg:absolute ${
className={`max-md:p-5 flex max-md:flex-col lg:flex-col md:gap-3 max-md:gap-y-7 gap-y-3 max-md:rounded-2xl max-md:h-full select-none max-md:[background:radial-gradient(ellipse_at_100%_100%,#7A7A7A33,transparent)] max-md:[backdrop-filter:blur(500px)] lg:absolute ${
page === 'main'
? 'lg:w-[31.875vw] h-full'
: 'lg:w-[23.611vw] h-[40.556vw]'
+4 -10
View File
@@ -15,13 +15,13 @@ export function ThreeDTour({
}) {
const opacityMain = useTransform(
scrollProgress,
[0, 1 / 5, 1.5 / 5],
[0, 1 / (videos.length - 1), 1.5 / (videos.length - 1)],
[0, 1, 0]
);
const opacityPrime = useTransform(
scrollProgress,
[0, 1 / (primeVideos.length + 8)],
[0, 1 / (primeVideos.length + 7) / 2],
[1, 0]
);
@@ -31,12 +31,6 @@ export function ThreeDTour({
['0%', '-100%']
);
const xPrime = useTransform(
scrollProgress,
[0, 1 / (primeVideos.length + 8)],
['0%', '100%']
);
const y = useTransform(
scrollProgress,
[0, 1 / (videos.length - 1)],
@@ -55,10 +49,10 @@ export function ThreeDTour({
style={{
opacity: page === 'main' ? opacityMain : opacityPrime,
y: page === 'main' ? y : undefined,
x: page === 'main' ? xMain : xPrime,
x: page === 'main' ? xMain : undefined,
}}
className={`p-[1.667vw] max-lg:hidden rounded-[1.111vw] [background:radial-gradient(ellipse_at_100%,#7A7A7A33,transparent)] [backdrop-filter:blur(500px)] aspect-[340/368] flex flex-col justify-between gap-[4.444vw] absolute w-[23.611vw] ${
page === 'prime' ? 'right-[1.389vw] h-[40.556vw]' : 'h-full'
page === 'prime' ? 'left-[1.389vw] h-[40.556vw]' : 'h-full'
}`}
>
<p className="heading2 font-medium">
+1 -1
View File
@@ -123,7 +123,7 @@ export function VideoLayerMain({ scroll }: { scroll: MotionValue<number> }) {
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="w-full h-[5.556vw] bg-gradient-to-t from-[#0F1011] absolute bottom-0 z-10"
className="w-full h-[5.556vw] bg-gradient-to-t from-[#0F1011] absolute -bottom-1"
/>
</Fragment>
))}
+130 -8
View File
@@ -25,7 +25,7 @@ export function VideoLayerPrime({ scroll }: { scroll: MotionValue<number> }) {
setSlide(
Math.min(
Math.trunc(value * (primeVideos.length + 9)),
primeVideos.length + 8
primeVideos.length + 7
)
)
);
@@ -34,27 +34,144 @@ export function VideoLayerPrime({ scroll }: { scroll: MotionValue<number> }) {
const handleOnViewportFeatureEnter = () => {
if (isViewportEntered) return;
setIsViewportEntered(true);
};
const y = useTransform(
scroll,
[6 / (primeVideos.length + 8), 7 / (primeVideos.length + 8)],
[6 / (primeVideos.length + 7), 7 / (primeVideos.length + 7)],
['0%', '-100%']
);
const x = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
6 / (primeVideos.length + 7),
],
['32.708vw', '32.708vw', '0vw', '0vw', '30vw', '30vw', '0vw']
);
const width = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['64.444vw', '61.667vw', '64.444vw', '64.444vw', '61.667vw', '65vw']
);
const backgroundSize = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['48.333vw', '53.962vw', '53.962vw', '62.946vw', '47.813vw', '53.962vw']
);
const videoWidth = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['39.822vw', '44.46vw', '44.46vw', '51.861vw', '39.392vw', '44.46vw']
);
const opacity = useTransform(
scroll,
[
3.5 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
4.5 / (primeVideos.length + 7),
],
[0, 1, 0]
);
const videoHeight = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['19vw', '21vw', '21vw', '24.5vw', '18.5vw', '21vw']
);
const backgroundPositionY = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['0vw', '0vw', '0vw', '-2.569vw', '7.292vw', '0vw']
);
const top = useTransform(
scroll,
[
0,
1 / (primeVideos.length + 7),
2 / (primeVideos.length + 7),
3 / (primeVideos.length + 7),
4 / (primeVideos.length + 7),
5 / (primeVideos.length + 7),
],
['3.602vw', '4.022vw', '4.022vw', '2.122vw', '10.855vw', '4.022vw']
);
return (
<motion.div
style={{ y }}
style={{
y,
backgroundPositionY,
width,
x,
backgroundSize,
perspective: videoHeight,
}}
viewport={{ margin: '-10% 0% 0% 0%', once: true }}
onViewportEnter={handleOnViewportFeatureEnter}
className="absolute overflow-hidden h-[40.556vw] w-[97.222vw] bg-[url(/img/pages/home/presentation/touch_screen.png)] bg-no-repeat bg-[length:48.333vw] bg-top perspective-[19.313vw]"
className="absolute overflow-hidden h-[40.556vw] bg-[url(/img/pages/home/presentation/touch_screen.png)] bg-no-repeat bg-[length:48.333vw] bg-top"
>
{!!videoRefs.length &&
primeVideos.map(({ src }, index) =>
src ? (
<>
{index === slide && slide > 2 && (
<motion.p
style={{ opacity }}
className="absolute left-[1.667vw] top-[1.667vw] z-10 max-w-[34.097vw] heading2 font-medium"
>
Graff.estate легко встраивается в существующую цепочку продаж,
собирает данные о пользователе и его поведении
</motion.p>
)}
<motion.video
key={index}
src={
@@ -66,19 +183,24 @@ export function VideoLayerPrime({ scroll }: { scroll: MotionValue<number> }) {
loop
muted
playsInline
style={{ zIndex: primeVideos.length + 8 - index }}
style={{
zIndex: primeVideos.length + 7 - index,
width: videoWidth,
height: videoHeight,
top,
}}
className={`absolute w-[39.822vw] h-[19vw] origin-top object-bottom object-cover top-[3.602vw] !rotate-x-4 left-1/2 -translate-x-1/2 transition-opacity${
slide > index && slide < primeVideos.length
? ' opacity-0'
: ''
}`}
/>
<div className="h-[10.625vw] w-[25.208vw] [background:linear-gradient(180deg,#23242500_0%,#232425_71.28%)]m bg-gradient-to-t from-[#0f1011] via-75% z-10 left-1/2 -translate-x-1/2 bottom-0 absolute" />
<div className="h-[5.5vw] w-full bg-gradient-to-t from-[#0f1011] via-75% left-1/2 -translate-x-1/2 bottom-0 absolute" />
</>
) : (
<div
key={index}
style={{ zIndex: primeVideos.length + 8 - index }}
style={{ zIndex: primeVideos.length + 7 - index }}
className={`inset-0 transition-opacity${
slide > index ? ' opacity-0' : ''
}`}
+33 -5
View File
@@ -1,42 +1,70 @@
export const videos = [
{ src: '1_search', anchorImg: '/img/pages/prime/search-select.jpg' },
{ src: '2_3dtour', anchorImg: '/img/pages/prime/3dtour.jpg' },
{
src: '1_search',
anchorImg: '/img/pages/prime/search-select.jpg',
title: 'Поиск',
},
{
src: '2_3dtour',
anchorImg: '/img/pages/prime/3dtour.jpg',
title: '3Д-тур',
},
{
src: '3_infrastructure',
anchorImg: '/img/pages/prime/infra.jpg',
title: 'Инфраструктура',
},
{
src: '4_insolation',
anchorImg: '/img/pages/prime/insolation.jpg',
title: 'Инсоляция',
},
{
src: '5_engine',
anchorImg: '/img/pages/prime/engine.jpg',
title: 'Инженерные системы',
},
{ src: '5_engine', anchorImg: '/img/pages/prime/engine.jpg' },
{
src: '6_reservation',
anchorImg: '/img/pages/prime/crm.jpg',
title: 'Интеграция с CRM',
},
];
export const primeVideos = [
{ src: '2_3dtour', anchorImg: '/img/pages/prime/3dtour.jpg' },
{
src: '2_3dtour',
anchorImg: '/img/pages/prime/3dtour.jpg',
title: '3Д-тур',
},
{
src: '1_search',
anchorImg: '/img/pages/prime/search-select.jpg',
title: 'Поиск',
},
{
src: '3_infrastructure',
anchorImg: '/img/pages/prime/infra.jpg',
title: 'Инфраструктура',
},
{
src: '2_3dtour',
anchorImg: '/img/pages/prime/favorites.jpg',
title: 'Избранное',
},
{
src: '6_reservation',
anchorImg: '/img/pages/prime/crm.jpg',
title: 'Интеграция с CRM',
},
{
src: '4_insolation',
anchorImg: '/img/pages/prime/insolation.jpg',
title: 'Инсоляция',
},
{
src: '6_reservation',
anchorImg: '/img/pages/prime/cp.jpg',
title: 'Отправка КП',
},
{ src: '6_reservation', anchorImg: '/img/pages/prime/cp.jpg' },
];
+11 -4
View File
@@ -26,7 +26,7 @@ export function PrimeProgressItem({
<motion.div
animate={canAnimate ? { x, y, rotateZ, zIndex } : {}}
transition={{ bounce: 'none' }}
className="group max-lg:hidden flex items-center z-10"
className="group max-lg:hidden flex items-center z-10 relative"
>
<motion.div
animate={{ opacity: +!canAnimate }}
@@ -46,9 +46,7 @@ export function PrimeProgressItem({
? '#37393B'
: '#FFFFFF',
}}
className={
'p-[0.278vw] border relative cursor-pointer rounded-[1.111vw]'
}
className="p-[0.278vw] border relative cursor-pointer rounded-[1.111vw]"
>
<img
src={src}
@@ -65,6 +63,15 @@ export function PrimeProgressItem({
: 'bg-gradient-to-r from-white to-[#37393B]')
}
/>
<motion.div
initial={{ opacity: 0, y: 100 }}
animate={{ opacity: +active, y: active ? 0 : 100 }}
transition={{ bounce: 'none' }}
className="flex flex-col items-center gap-[0.278vw] absolute -top-1/2 left-1/2 min-w-full -z-5 -translate-x-1/2 group-last:translate-x-[calc(-50%+0.4165vw)] group-first:translate-x-[calc(-50%-0.4165vw)]"
>
<p className="btnm font-medium text-centera text-nowrap">{title}</p>
<motion.div className="w-[0.069vw] h-[0.556vw] rounded-[0.139vw] bg-[#7A7A7A]" />
</motion.div>
</motion.div>
);
}