diff --git a/public/img/pages/about/team/3ds.png b/public/img/pages/about/team/3ds.png index 76cc99ff..04dfca51 100644 Binary files a/public/img/pages/about/team/3ds.png and b/public/img/pages/about/team/3ds.png differ diff --git a/public/img/pages/about/team/devs.png b/public/img/pages/about/team/devs.png index 713e8b33..283e40f3 100644 Binary files a/public/img/pages/about/team/devs.png and b/public/img/pages/about/team/devs.png differ diff --git a/src/app/globals.css b/src/app/globals.css index 8515ac1a..6339fab6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -94,12 +94,12 @@ html { @apply text-[clamp(14px,14px+(100vw-360px)/1240*2,16px)] leading-none; } - .headline1{ - @apply font-medium text-[1.667vw] leading-[1.944vw] tracking-[-0.02em] md:max-lg:text-[3.125vw] md:max-lg:leading-[3.646vw] max-md:text-[6.667vw] max-md:leading-[7.778vw] + .headline1 { + @apply font-medium text-[1.667vw] leading-[1.944vw] tracking-[-0.02em] md:max-lg:text-[3.125vw] md:max-lg:leading-[3.646vw] max-md:text-[6.667vw] max-md:leading-[7.778vw]; } - .headline2{ - @apply font-medium text-[1.389vw] leading-[1.667vw] tracking-[-0.02em] md:max-lg:text-[2.083vw] md:max-lg:leading-[2.344vw] max-md:text-[4.444vw] max-md:leading-[4.444vw] max-md:font-normal + .headline2 { + @apply font-medium text-[1.389vw] leading-[1.667vw] tracking-[-0.02em] md:max-lg:text-[2.083vw] md:max-lg:leading-[2.344vw] max-md:text-base max-md:leading-[4.444vw] max-md:font-normal; } .text-gradient { diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index db0158d6..44514296 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -15,7 +15,6 @@ import { useOnClickOutside } from "usehooks-ts"; import { Products } from "./Products"; import { useAuthStore } from "@/stores/useAuthStore"; import { getQueryClient } from "@/lib/queryClient"; -import { LogoIcon } from "../icons/LogoIcon"; import GraffIcon from "../icons/GraffIcon"; import CloseIcon from "../icons/CloseIcon"; import BurgerIcon from "../icons/BurgerIcon"; @@ -66,13 +65,13 @@ export function Header() {
-
+
-

Контакты:

+

Контакты:

8 800 770 00 67 @@ -237,7 +236,7 @@ export function Header() { alt="кейс dprofile" className="absolute bottom-0 left-0 rotate-[2.56deg]" /> -

Смотреть кейс

+

Смотреть кейс

) : (
diff --git a/src/components/pages/AboutPage/AboutAchievementsCard.tsx b/src/components/pages/AboutPage/AboutAchievementsCard.tsx index 02491c6a..fe9aa4af 100644 --- a/src/components/pages/AboutPage/AboutAchievementsCard.tsx +++ b/src/components/pages/AboutPage/AboutAchievementsCard.tsx @@ -1,6 +1,7 @@ /* eslint-disable @next/next/no-img-element */ +import { useMediaQueries } from "@/hooks/useMediaQueries"; import { motion } from "framer-motion"; -import React from "react"; +import React, { useRef, useEffect, useState } from "react"; export default function AboutAchievementsCard({ img, @@ -15,29 +16,79 @@ export default function AboutAchievementsCard({ imageClassName?: string; labelClassName?: string; }) { + const cardRef = useRef(null); + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); + const [cardTransform, setCardTransform] = useState({ x: 0, y: 0 }); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + setMousePosition({ x: e.clientX, y: e.clientY }); + }; + + window.addEventListener("mousemove", handleMouseMove); + return () => window.removeEventListener("mousemove", handleMouseMove); + }, []); + + const { isLg } = useMediaQueries(); + + useEffect(() => { + if (!cardRef.current || !isLg) return; + + const card = cardRef.current; + const rect = card.getBoundingClientRect(); + const cardCenterX = rect.left + rect.width / 2; + const cardCenterY = rect.top + rect.height / 2; + + const deltaX = mousePosition.x - cardCenterX; + const deltaY = mousePosition.y - cardCenterY; + const distance = Math.hypot(deltaX, deltaY); + + const maxDistance = 1000; + const maxOffset = 20; + + if (distance < maxDistance) { + const strength = 1 - distance / maxDistance; + const offsetX = (deltaX / distance) * strength * maxOffset; + const offsetY = (deltaY / distance) * strength * maxOffset; + + setCardTransform({ + x: isNaN(offsetX) ? 0 : offsetX, + y: isNaN(offsetY) ? 0 : offsetY, + }); + } else { + setCardTransform({ x: 0, y: 0 }); + } + }, [isLg, mousePosition]); + return ( -
-

{title}

-
+ ); } diff --git a/src/components/pages/AboutPage/AboutExperience.tsx b/src/components/pages/AboutPage/AboutExperience.tsx index a6ee0433..34a440d5 100644 --- a/src/components/pages/AboutPage/AboutExperience.tsx +++ b/src/components/pages/AboutPage/AboutExperience.tsx @@ -10,14 +10,14 @@ import { ArticleCard } from "../BlogPage/ArticleCard"; import { useSearchParams } from "next/navigation"; import { useGetArticlesQuery } from "@/queries/getArticles"; import AchievementsCardsMobile from "./AchievementsCardsMobile"; -import { useParallax } from "@/hooks/useParallax"; +import { motion } from "framer-motion"; export default function AboutExperience() { const searchParams = useSearchParams(); - const parallaxRef = useRef(null); - const { data: articles } = useGetArticlesQuery(searchParams.getAll("tags")); - useParallax(parallaxRef); + const parallaxRef = useRef(null); + + const { data: articles } = useGetArticlesQuery(searchParams.getAll("tags")); return ( <> @@ -39,7 +39,10 @@ export default function AboutExperience() { md:max-lg:grid-cols-2 md:max-lg:grid-rows-4 md:max-lg:[grid-template-areas:'a_d'_'b_b'_'b_b'_'c_e'] max-md:flex max-md:flex-col max-md:gap-[3.333vw]" > -
- + Архитектурные
визуализации
-
-
+ - + Удаленная демонстрация -
-
+ Интерактивные
презентации -
-
+ - Сайты + Сайты -
-
+ - + Виртуальные туры
по 360 сферам
-
+
@@ -121,7 +136,7 @@ export default function AboutExperience() { md:max-lg:text-[5.208vw] max-md:text-[8.889vw] max-md:mb-[11.111vw]" > - Каждый год мы принимаем участие
{" "} + Каждый год мы принимаем участие
{" "} в самых крупных российских выставках @@ -131,7 +146,7 @@ export default function AboutExperience() {

- За нашей спиной участие
в 40 + За нашей спиной участие
в 40 выставках

@@ -160,11 +175,11 @@ export default function AboutExperience() {

- + Ближайшие мероприятия,
где каждый девелопер может лично протестировать{" "} -
и оценить функционал +
и оценить функционал наших интерактивных макетов недвижимости

@@ -208,32 +223,32 @@ export default function AboutExperience() {
diff --git a/src/components/pages/AboutPage/AboutHero.tsx b/src/components/pages/AboutPage/AboutHero.tsx index bc357f98..90ed1f3f 100644 --- a/src/components/pages/AboutPage/AboutHero.tsx +++ b/src/components/pages/AboutPage/AboutHero.tsx @@ -18,17 +18,18 @@ export function AboutHero() { max-lg:relative md:max-lg:aspect-[736/441] max-md:aspect-[340/512] max-md:rounded-b-[4.444vw]" > +

- GRAFF.estate —
IT компания, -
разрабатывающая
{" "} + GRAFF.estate —
IT компания, +
разрабатывающая
{" "} передовые
инструменты продаж{" "} -
для{" "} +
для{" "}
застройщиков

diff --git a/src/components/pages/AboutPage/AboutMain.tsx b/src/components/pages/AboutPage/AboutMain.tsx index 489a8963..8aadae6c 100644 --- a/src/components/pages/AboutPage/AboutMain.tsx +++ b/src/components/pages/AboutPage/AboutMain.tsx @@ -1,9 +1,9 @@ "use client"; import React, { useEffect } from "react"; import { AboutHero } from "./AboutHero"; -import AboutExperience from "./AboutExperience"; import { Statistics } from "../MainPage/Statistics"; import AboutTeam from "./AboutTeam"; +import dynamic from "next/dynamic"; export default function AboutMain() { useEffect(() => { @@ -23,6 +23,13 @@ export default function AboutMain() { }; }, []); + const AboutExperience = dynamic( + () => import("./AboutExperience").then((mod) => mod.default), + { + ssr: false, + } + ); + return (
diff --git a/src/components/pages/AboutPage/AboutTeam.tsx b/src/components/pages/AboutPage/AboutTeam.tsx index 2ac66735..003a1ab6 100644 --- a/src/components/pages/AboutPage/AboutTeam.tsx +++ b/src/components/pages/AboutPage/AboutTeam.tsx @@ -5,13 +5,14 @@ import { motion, useInView } from "framer-motion"; function TeamCarousel() { return ( -
+
- {Array.from({ length: 20 }).map((_, index) => ( + + {/* {Array.from({ length: 20 }).map((_, index) => (
))} -
+ */}
); @@ -91,7 +92,7 @@ export default function AboutTeam() { {" "} более 60 высококвалифицирова -
+
ных специалистов
{" "} в своем офисе в Екатеринбурге @@ -102,7 +103,7 @@ export default function AboutTeam() { max-md:w-full max-md:text-[4.444vw] max-md:leading-[5vw] max-md:mb-[13.333vw]" > Используем только собственные силы,{" "} -
не передавая задачи на аутсорс +
не передавая задачи на аутсорс
3 @@ -212,12 +213,12 @@ export default function AboutTeam() {

сотрудников в отделе
3D-моделирования, сколько{" "} -
{" "} -
точно,{" "} +
{" "} +
точно,{" "}
никто не считал

- {/* */} +
diff --git a/src/components/pages/AboutPage/AchievementsCardsMobile.tsx b/src/components/pages/AboutPage/AchievementsCardsMobile.tsx index 4037b981..f5afbdfa 100644 --- a/src/components/pages/AboutPage/AchievementsCardsMobile.tsx +++ b/src/components/pages/AboutPage/AchievementsCardsMobile.tsx @@ -17,7 +17,7 @@ export default function AchievementsCardsMobile({ ) : (
diff --git a/src/components/pages/WebPage/PageComponents/Accordeon.tsx b/src/components/pages/WebPage/PageComponents/Accordeon.tsx index 7884da85..867fa99a 100644 --- a/src/components/pages/WebPage/PageComponents/Accordeon.tsx +++ b/src/components/pages/WebPage/PageComponents/Accordeon.tsx @@ -79,23 +79,22 @@ export default function Accordeon({ return ( onOpenChange(title)} - // layout // Enable layout animations initial={{ height: `${closedElHeight}vw` }} animate={{ height: opened ? `${openedElHeight}vw` : `${closedElHeight}vw`, }} transition={{ duration: 0.5, ease: "easeInOut" }} > - - {title} + + {title}
{content.map((item, index) => ( void; } export default function WebHeroControlBtn({ @@ -20,7 +20,7 @@ export default function WebHeroControlBtn({ }: ControlBtnProps) { return ( callback && callback()} + onClick={callback} style={{ background: !active ? "linear-gradient(#37393B99 0%, #37393B99 100%)" diff --git a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideCards.tsx b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideCards.tsx index b9ad65c5..02d572e3 100644 --- a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideCards.tsx +++ b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideCards.tsx @@ -1,4 +1,4 @@ -import { useParallax } from "@/hooks/useParallax"; +import { useMediaQueries } from "@/hooks/useMediaQueries"; import { motion, AnimatePresence } from "framer-motion"; import { useRef } from "react"; @@ -12,19 +12,36 @@ export default function ContentSlideCards({ index?: number; }) { const CardsRef = useRef(null); - useParallax(CardsRef); + + const { isLg, isMd } = useMediaQueries(); return ( {active && ( @@ -41,12 +58,10 @@ export default function ContentSlideCards({ animate={{ x: 0, y: 0 }} exit={{ x: "4.167vw", y: "-4.167vw" }} transition={{ duration: 0.6, ease: "easeInOut" }} - className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute left-[5.25vw] bottom-[2.625vw]" + className="lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:gap-[0.694vw] gap-2 lg:p-[1.389vw] p-4 bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute lg:left-[5.25vw] lg:bottom-[2.625vw] left-0 bottom-0" > - - Для соцсетей: - -

+ Для соцсетей: +

Вертикальные изображения с идеально выстоенным кадром

@@ -56,12 +71,10 @@ export default function ContentSlideCards({ animate={{ x: 0, y: 0 }} exit={{ x: "-4.167vw", y: "4.167vw" }} transition={{ duration: 0.6, ease: "easeInOut" }} - className="w-[13.403vw] aspect-[166/80] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute right-[5.25vw] top-[2.625vw]" + className="lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:gap-[0.694vw] gap-2 lg:p-[1.389vw] p-4 bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute lg:right-[5.25vw] lg:top-[2.625vw] right-0 top-0" > - - Наружной рекламы: - -

+ Наружной рекламы: +

Высокое разрешение для широкоформатной печати

@@ -72,12 +85,18 @@ export default function ContentSlideCards({ @@ -86,22 +105,34 @@ export default function ContentSlideCards({ animate={{ translateX: "0px", translateY: "0px", - width: active ? "22.986vw" : "15.833vw", + width: isLg + ? active + ? "23.063vw" + : "15.882vw" + : isMd + ? "24.919vw" + : "191.38px", }} transition={{ duration: 0.4, ease: "easeInOut" }} - className="w-[22.986vw] aspect-[331/222] absolute z-[2]" + className="absolute z-[2] lg:rounded-[0.417vw] rounded-2xl" src="/img/pages/web/content/card_md.png" alt="" /> diff --git a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideHall.tsx b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideHall.tsx index 1bf4c3bd..597f7c65 100644 --- a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideHall.tsx +++ b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideHall.tsx @@ -1,4 +1,4 @@ -import { useParallax } from "@/hooks/useParallax"; +import { useMediaQueries } from "@/hooks/useMediaQueries"; import { motion } from "framer-motion"; import { useRef } from "react"; @@ -12,31 +12,45 @@ export default function ContentSlideHall({ index?: number; }) { const HallRef = useRef(null); - useParallax(HallRef); + const { isLg, isMd } = useMediaQueries(); return (
- - По двору: - -

+ По двору: +

возможность ощутить ритм жизни Жилого комплекса и его атмосферу

@@ -45,12 +59,10 @@ export default function ContentSlideHall({ animate={{ opacity: active ? 1 : 0, }} - className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute right-[5.25vw] top-[2.625vw]" + className="lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:gap-[0.694vw] gap-2 lg:p-[1.389vw] p-4 bg-[#37393B99] backdrop-blur-[5px] lg:rounded-[1.111vw] rounded-2xl absolute lg:right-[5.25vw] lg:top-[2.625vw] right-0 top-0" > - - По квартире: - -

+ По квартире: +

возможность оценить пространство в объеме и ощутить себя внутри

@@ -58,15 +70,31 @@ export default function ContentSlideHall({ diff --git a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideIphone.tsx b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideIphone.tsx index 050fc0e1..5ac78cba 100644 --- a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideIphone.tsx +++ b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/ContentSlideIphone.tsx @@ -1,3 +1,4 @@ +import { useMediaQueries } from "@/hooks/useMediaQueries"; import { useParallax } from "@/hooks/useParallax"; import { motion, AnimatePresence } from "framer-motion"; import { useRef } from "react"; @@ -12,33 +13,49 @@ export default function ContentSlideIphone({ index?: number; }) { const IphoneRef = useRef(null); - useParallax(IphoneRef); + // useParallax(IphoneRef); + + const { isMd, isLg } = useMediaQueries(); return ( -
+
- - Яркие видео - -

+ Яркие видео +

Дайте клиентам почувствовать атмосферу жизни в жилом комплексе

@@ -46,15 +63,13 @@ export default function ContentSlideIphone({ - - Интерьеры - -

+ Интерьеры +

Поможем выгодно показать клиентам ваши дизайнерские решения

@@ -62,15 +77,19 @@ export default function ContentSlideIphone({ - - Экстерьер - -

+ Экстерьер +

Запечатлим лучшие места для досуга и отдыха в вашем проекте

@@ -78,15 +97,13 @@ export default function ContentSlideIphone({ - - Архитектура - -

+ Архитектура +

Сделаем видеооблеты, чтобы показать клиентам жилой комплекс в инфраструктуре города

@@ -103,7 +120,7 @@ export default function ContentSlideIphone({ : "/img/pages/web/content/iphone_disabled.png" } alt="" - className="w-auto h-full mx-auto" + className="mx-auto w-auto h-full lg:rounded-[0.417vw] rounded-2xl" />
diff --git a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/WebInfiniteSlider.tsx b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/WebInfiniteSlider.tsx index 7b953d27..3570aa64 100644 --- a/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/WebInfiniteSlider.tsx +++ b/src/components/pages/WebPage/PageComponents/WebInfiniteSlider/WebInfiniteSlider.tsx @@ -4,10 +4,18 @@ import { useState, useEffect } from "react"; import ContentSlideCards from "./ContentSlideCards"; import ContentSlideHall from "./ContentSlideHall"; import ContentSlideIphone from "./ContentSlideIphone"; +import { useMediaQueries } from "@/hooks/useMediaQueries"; +import { useSwipeable } from "react-swipeable"; export default function InfiniteSlider() { - const offsetWidth = 23.661 + 0.486; - const [slideOffset, setSlideOffset] = useState(-offsetWidth); + const { isLg, isMd } = useMediaQueries(); + const offset = isLg ? 0 : isMd ? 10.677 : 95.833; + const slideWidth = isLg + ? 23.661 + 0.486 + : isMd + ? 31.25 + 1.0415 + : 94.444 + 1.389; + const [slideOffset, setSlideOffset] = useState(-slideWidth); const [isAnimating, setIsAnimating] = useState(false); const [slides, setSlides] = useState([ { type: "hall", id: Math.random() }, @@ -18,8 +26,8 @@ export default function InfiniteSlider() { ]); useEffect(() => { - setSlideOffset(-offsetWidth); - }, [slides]); + setSlideOffset(-slideWidth - offset); + }, [offset, slideWidth, slides]); function moveSlide(direction: "left" | "right") { if (isAnimating) return; @@ -30,19 +38,32 @@ export default function InfiniteSlider() { ? [...slides.slice(1), { ...slides[2], id: Math.random() }] : [{ ...slides[2], id: Math.random() }, ...slides.slice(0, -1)] ); - setSlideOffset(direction === "right" ? 0 : -offsetWidth * 2); + setSlideOffset( + (prev) => prev + (direction === "right" ? 1 : -1) * slideWidth + ); } + const handlers = useSwipeable({ + onSwipedLeft: () => moveSlide("right"), + onSwipedRight: () => moveSlide("left"), + preventScrollOnSwipe: true, + trackTouch: true, + trackMouse: true, + }); + return ( -
-
+
+
setIsAnimating(false)} - className="flex gap-[0.972vw]" + className="flex lg:gap-[0.972vw] md:gap-[2.083vw] gap-[2.778vw]" style={{ transform: `translateX(${slideOffset}vw)`, transitionDuration: `${ - slideOffset === 0 || slideOffset === -offsetWidth * 2 ? 0 : 0.5 + slideOffset === -offset || + slideOffset === -slideWidth * 2 - offset + ? 0 + : 0.5 }s`, }} > @@ -78,18 +99,18 @@ export default function InfiniteSlider() {
diff --git a/src/components/pages/WebPage/WebHero.tsx b/src/components/pages/WebPage/WebHero.tsx index 93cc777f..fc6d60f7 100644 --- a/src/components/pages/WebPage/WebHero.tsx +++ b/src/components/pages/WebPage/WebHero.tsx @@ -1,6 +1,6 @@ /* eslint-disable @next/next/no-img-element */ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import SearchIcon from "@/components/icons/SearchIcon"; import GenPlanSearchIcon from "@/components/icons/GenPlanSearchIcon"; import InfoIcon from "@/components/icons/InfoIcon"; @@ -8,75 +8,60 @@ import TreeIcon from "@/components/icons/TreeIcon"; import FavoriteIcon from "@/components/icons/FavoriteIcon"; import ReportIcon from "@/components/icons/ReportIcon"; import WebHeroControlBtn from "./PageComponents/WebHeroControlBtn"; -import { motion } from "framer-motion"; +import { AnimatePresence, motion } from "framer-motion"; export default function WebHero() { + const videoRef = useRef(null); + const [currentVideo, setCurrentVideo] = useState(0); - const [autoPlay, setAutoPlay] = useState(true); - const videoRef = useRef(null); const [playbackProgress, setPlaybackProgress] = useState(0); - useEffect(() => { + const updateProgress = useCallback(() => { if (!videoRef.current) return; + const { currentTime, duration } = videoRef.current; + const progressPercent = duration ? (currentTime / duration) * 100 : 0; - videoRef.current.play(); + setPlaybackProgress(progressPercent); }, [currentVideo]); + // Обработчик timeupdate напрямую в JSX через onTimeUpdate + const handleTimeUpdate = useCallback(() => { + updateProgress(); + }, [updateProgress]); + + // Сбрасываем прогресс при переключении видео useEffect(() => { - if (!videoRef) return; - - const updateProgress = () => { - if (videoRef.current) { - const { currentTime, duration } = videoRef.current; - const progressPercent = duration ? (currentTime / duration) * 100 : 0; - setPlaybackProgress(progressPercent); - } - }; - videoRef.current && - videoRef.current.addEventListener("timeupdate", updateProgress); setPlaybackProgress(0); + }, [currentVideo]); - return () => { - videoRef.current?.removeEventListener("timeupdate", updateProgress); - }; - }, [videoRef]); - - const onVideoEnd = () => { - if (!autoPlay) return; - setCurrentVideo((cur) => (cur + 1) % 5); - }; - - const btnCallback = (video: number) => { - if (autoPlay) { - setPlaybackProgress(0); - setCurrentVideo(video); - setAutoPlay(false); - } else if (video === currentVideo) { - setAutoPlay(true); - } else { - setPlaybackProgress(0); - setCurrentVideo(video); - } - }; + const onVideoEnd = () => setCurrentVideo((cur) => (cur + 1) % 6); return (

Создаем интерактивные сайты для продажи недвижимости

-
+
- + setPlaybackProgress(0)} + loop + playsInline + muted + autoPlay + src={`/videos/pages/web/hero_${currentVideo}.mp4`} + className="absolute w-[50.694vw] aspect-[730/472] top-[4vw] left-[6.458vw] md:max-lg:w-[calc(87.2%)] md:max-lg:md:aspect-[585/345] md:max-lg:top-[1.5vw] md:max-lg:left-[6.458vw]" - /> + /> +
-
+
} className="top-[14.722vw] left-0" - active={!autoPlay && currentVideo === 0} - callback={() => btnCallback(0)} - progress={playbackProgress} + active={currentVideo === 0} + callback={() => setCurrentVideo(0)} + progress={currentVideo === 0 ? playbackProgress : 0} /> } className="top-[20.486vw] left-[8.194vw]" - active={!autoPlay && currentVideo === 1} - callback={() => btnCallback(1)} - progress={playbackProgress} + active={currentVideo === 1} + callback={() => setCurrentVideo(1)} + progress={currentVideo === 1 ? playbackProgress : 0} /> } className="top-[17.5vw] left-[16.389vw]" - active={!autoPlay && currentVideo === 2} - callback={() => btnCallback(2)} - progress={playbackProgress} + active={currentVideo === 2} + callback={() => setCurrentVideo(2)} + progress={currentVideo === 2 ? playbackProgress : 0} /> } className="top-[17.5vw] right-[16.389vw]" - active={!autoPlay && currentVideo === 3} - callback={() => btnCallback(3)} - progress={playbackProgress} + active={currentVideo === 3} + callback={() => setCurrentVideo(3)} + progress={currentVideo === 3 ? playbackProgress : 0} /> } className="top-[20.486vw] right-[8.194vw]" - active={!autoPlay && currentVideo === 4} - callback={() => btnCallback(4)} - progress={playbackProgress} + active={currentVideo === 4} + callback={() => setCurrentVideo(4)} + progress={currentVideo === 4 ? playbackProgress : 0} /> } className="top-[14.722vw] right-0" - active={!autoPlay && currentVideo === 5} - callback={() => btnCallback(5)} - progress={playbackProgress} + active={currentVideo === 5} + callback={() => setCurrentVideo(5)} + progress={currentVideo === 5 ? playbackProgress : 0} />
diff --git a/stagewise.json b/stagewise.json new file mode 100644 index 00000000..5392d136 --- /dev/null +++ b/stagewise.json @@ -0,0 +1,6 @@ +{ + "port": 3100, + "appPort": 3000, + "autoPlugins": true, + "plugins": [] +} \ No newline at end of file