This commit is contained in:
2025-08-08 19:54:40 +05:00
parent 5b9d3c7829
commit cbc92e4aaf
18 changed files with 405 additions and 244 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 102 KiB

+2 -2
View File
@@ -95,11 +95,11 @@ html {
}
.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]
@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
@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 {
+8 -9
View File
@@ -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() {
<Link
href={"/"}
ref={logoRef}
className="outline-none cursor-pointer max-lg:hidden"
className="max-lg:hidden cursor-pointer outline-none"
>
<div className="w-[13.333vw] aspect-[4/1]">
<LogoHorIcon />
</div>
</Link>
<div className="relative flex justify-center flex-1 m-auto">
<div className="flex relative flex-1 justify-center m-auto">
<AnimatePresence>
<motion.nav
ref={navRef}
@@ -93,7 +92,7 @@ export function Header() {
)}
<div
ref={productsBtnRef}
className="flex items-center max-md:hidden"
className="max-md:hidden flex items-center"
>
<button
className={
@@ -122,7 +121,7 @@ export function Header() {
href={"/projects"}
text={"Проекты"}
/>
<div className="flex justify-center flex-1 md:justify-end">
<div className="md:justify-end flex flex-1 justify-center">
<Link
href={"/form"}
className="btnm bg-gradient font-medium lg:px-[1.667vw] lg:py-[1.181vw] py-[17px] px-6 text-nowrap lg:rounded-[1.111vw] rounded-2xl flex items-center"
@@ -130,7 +129,7 @@ export function Header() {
Оставить заявку
</Link>
</div>
<div className="flex md:hidden md:justify-enda" ref={burgerBtnRef}>
<div className="md:hidden md:justify-enda flex" ref={burgerBtnRef}>
<button
className="!border-none p-[18px] hover:bg-[#232425] rounded-2xl active:opacity-50 outline-none cursor-pointer"
onClick={() => setBurgerOpened((prev) => !prev)}
@@ -188,10 +187,10 @@ export function Header() {
<Products />
</div>
<div>
<p className="mb-1 btnm opacity-60">Контакты:</p>
<p className="btnm mb-1 opacity-60">Контакты:</p>
<Link
href={"tel:88007700067"}
className="font-medium outline-none accent"
className="accent font-medium outline-none"
>
8 800 770 00 67
</Link>
@@ -237,7 +236,7 @@ export function Header() {
alt="кейс dprofile"
className="absolute bottom-0 left-0 rotate-[2.56deg]"
/>
<p className="font-medium btnm">Смотреть кейс</p>
<p className="btnm font-medium">Смотреть кейс</p>
</a>
) : (
<div className="bg-[#B5F54E] p-[0.764vw] self-center rounded-[0.556vw] max-lg:hidden">
@@ -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<HTMLDivElement>(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 (
<div
<motion.div
ref={cardRef}
animate={
isLg
? {
x: cardTransform.x,
y: cardTransform.y,
}
: {}
}
transition={{
type: "spring",
stiffness: 150,
damping: 15,
mass: 0.1,
}}
className={
"w-[15.486vw] aspect-[223/240] rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] absolute overflow-hidden md:max-lg:p-[2.083vw] max-md:p-[4.444vw] " +
"w-[15.486vw] aspect-[223/240] group rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] absolute overflow-hidden md:max-lg:p-[2.083vw] max-md:p-[4.444vw] " +
className
}
>
<motion.img
style={{
scale: 1,
}}
whileHover={{
scale: 1.15,
}}
transition={{
duration: 0.3,
}}
<img
src={img}
className={
"z-10 w-[9.444vw] md:max-lg:w-[20.833vw] max-md:w-[36.944vw] my-auto " +
"z-10 w-[9.444vw] md:max-lg:w-[20.833vw] max-md:w-[36.944vw] my-auto group-hover:scale-110 transition-all duration-300 " +
imageClassName
}
alt=""
alt={title}
/>
<p
className={
@@ -47,6 +98,6 @@ export default function AboutAchievementsCard({
>
{title}
</p>
</div>
</motion.div>
);
}
@@ -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]"
>
<div
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
transition={{ delay: 0.4, bounce: 0, duration: 0.5 }}
className="[grid-area:a] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
max-md:p-[4.444vw] max-md:aspect-[340/320]"
@@ -51,25 +54,31 @@ export default function AboutExperience() {
max-md:w-[70vw]"
alt=""
/>
<span className="text-center headline1">
<span className="headline1 text-center">
Архитектурные <br /> визуализации
</span>
</div>
<div
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
transition={{ delay: 0.6, bounce: 0, duration: 0.5 }}
className="relative overflow-hidden [grid-area:d] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col-reverse items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
max-md:p-[4.444vw] max-md:aspect-[340/320]"
>
<span className="z-10 text-center headline1">
<span className="headline1 z-10 text-center">
Удаленная демонстрация
</span>
<img
src={"/img/pages/about/experience/demonstrartion.png"}
alt=""
className="absolute bottom-0 w-full max-lg:top-0"
className="max-lg:top-0 absolute bottom-0 w-full"
/>
</div>
<div
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
transition={{ bounce: 0, duration: 0.5 }}
className="relative [grid-area:b] rounded-2xl
max-lg:border max-lg:border-[#FFFFFF1A] md:max-lg:p-[3.125vw]
max-md:p-[4.444vw] max-md:aspect-[340/320]"
@@ -82,20 +91,26 @@ export default function AboutExperience() {
<span className="absolute text-center headline1 bottom-[3.125vw] left-1/2 -translate-x-1/2 z-10">
Интерактивные <br /> презентации
</span>
</div>
<div
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
transition={{ delay: 0.8, bounce: 0, duration: 0.5 }}
className="relative overflow-hidden [grid-area:c] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col-reverse items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
max-md:p-[4.444vw] max-md:aspect-[340/320]"
>
<span className="z-10 text-center headline1">Сайты</span>
<span className="headline1 z-10 text-center">Сайты</span>
<img
src={"/img/pages/about/experience/sites.png"}
alt=""
className="absolute bottom-0 h-full max-lg:top-0 max-md:w-full"
className="max-lg:top-0 max-md:w-full absolute bottom-0 h-full"
/>
</div>
<div
</motion.div>
<motion.div
initial={{ y: 100, opacity: 0 }}
whileInView={{ y: 0, opacity: 1, repeatCount: 0 }}
transition={{ delay: 1, bounce: 0, duration: 0.5 }}
className="[grid-area:e] w-full aspect-[340/360] rounded-2xl border border-[#FFFFFF1A] flex flex-col items-center justify-between p-[1.667vw]
md:max-lg:p-[3.125vw]
max-md:p-[4.444vw] max-md:aspect-[340/320]"
@@ -107,10 +122,10 @@ export default function AboutExperience() {
max-md:w-[71vw] "
alt=""
/>
<span className="text-center headline1">
<span className="headline1 text-center">
Виртуальные туры <br /> по 360 сферам
</span>
</div>
</motion.div>
</div>
</div>
@@ -121,7 +136,7 @@ export default function AboutExperience() {
md:max-lg:text-[5.208vw]
max-md:text-[8.889vw] max-md:mb-[11.111vw]"
>
Каждый год мы принимаем участие <br className="hidden max-lg:block" />{" "}
Каждый год мы принимаем участие <br className="max-lg:block hidden" />{" "}
<span className="text-transparent">
в самых крупных российских выставках
</span>
@@ -131,7 +146,7 @@ export default function AboutExperience() {
<div className="flex justify-between max-lg:flex-col md:max-lg:gap-[4.167vw]">
<div className="w-[23.611vw] max-lg:w-full max-md:mb-[8.889vw]">
<h3 className="headline1 mb-[0.833vw] mdLmax-lg:mb-[1.563vw] max-md:mb-[3.333vw]">
За нашей спиной участие <br className="hidden lg:block" /> в 40
За нашей спиной участие <br className="lg:block hidden" /> в 40
выставках
</h3>
<p className="headline1 text-[#7A7A7A]">
@@ -160,11 +175,11 @@ export default function AboutExperience() {
<div className="flex justify-between max-lg:flex-col md:max-lg:gap-[4.167vw] max-md:gap-[8.889vw]">
<p className="w-[23.611vw] headline1 text-[#7A7A7A] max-lg:w-full">
<span className="text-white headline1">
<span className="headline1 text-white">
Ближайшие мероприятия, <br />
</span>
где каждый девелопер может лично протестировать{" "}
<br className="hidden md:max-lg:block" /> и оценить функционал
<br className="md:max-lg:block hidden" /> и оценить функционал
наших интерактивных макетов недвижимости
</p>
<div className="flex justify-between gap-x-3 max-md:flex-col max-md:gap-[3.333vw]">
@@ -208,32 +223,32 @@ export default function AboutExperience() {
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/table.png"}
title="Наш продукт зарегистрирован и включен в реестр отечественного ПО"
className="top-0 left-[16.389vw] max-lg:hidden "
className="!top-0 left-[16.389vw] max-lg:hidden"
/>
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/wow.png"}
title="Заняли 1 место на WOW AWARDS 2024 совместно с застройщиком Upside Development"
className="top-[8.333vw] left-0 max-lg:hidden "
className="!top-[8.333vw] left-0 max-lg:hidden"
/>
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/sk.png"}
title="Мы резиденты Сколково"
className="bottom-0 left-[8.194vw] max-lg:hidden "
className="!bottom-0 left-[8.194vw] max-lg:hidden"
/>
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/map.png"}
title="Наши интерактивные столы стоят уже в 16 городах России"
className="top-0 right-[8.194vw] max-lg:hidden "
className="!top-0 right-[8.194vw] max-lg:hidden"
/>
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/dp.png"}
title="Каждый наш проект, получил одну или несколько наград на платформе Dprofile"
className="bottom-[7.708vw] right-0 max-lg:hidden "
className="!bottom-[7.708vw] right-0 max-lg:hidden"
/>
<AboutAchievementsCard
img={"/img/pages/about/experience/cards/build_up.png"}
title="В 2023 году мы выиграли Build UP в категории IT"
className="bottom-0 right-[16.389vw] max-lg:hidden "
className="!bottom-0 right-[16.389vw] max-lg:hidden"
/>
</div>
</div>
+4 -3
View File
@@ -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]"
></video>
<div className="bg-[#00000033] absolute inset-0 max-lg:hidden h-dvh" />
<div className="relative z-11 pt-[16.25vw] max-lg:pt-0 bg-clip-text">
<h1
className="text-center font-medium text-[6.667vw] leading-[95%] tracking-[-4%]
md:max-lg:tracking-[-0.02em] md:max-lg:leading-[85%] md:max-lg:text-[6.25vw]
max-md:text-[11.111vw] max-md:leading-[85%] max-md:tracking-[-0.04em]"
>
GRAFF.estate <br className="hidden max-md:block" /> IT компания,
<br /> разрабатывающая <br className="hidden md:max-lg:block" />{" "}
GRAFF.estate <br className="max-md:block hidden" /> IT компания,
<br /> разрабатывающая <br className="md:max-lg:block hidden" />{" "}
передовые
<br className="md:max-lg:hidden" /> инструменты продаж{" "}
<br className="hidden max-md:block" /> для{" "}
<br className="max-md:block hidden" /> для{" "}
<br className="max-lg:hidden" /> застройщиков
</h1>
+8 -1
View File
@@ -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 (
<div className="space-y-[11.111vw] md:max-lg:space-y-[13.021vw] max-md:space-y-[27.778vw]">
<AboutHero />
+11 -10
View File
@@ -5,13 +5,14 @@ import { motion, useInView } from "framer-motion";
function TeamCarousel() {
return (
<div className="right-[-7.333vw] top-[-10.5vw] absolute w-[23.611vw] aspect-[1/1] md:max-lg:w-[44.141vw] md:max-lg:right-[-30vw] md:max-lg:">
<div className="lg:right-[-11vw] lg:top-[-11vw] -right-1/3 -top-1/3 absolute lg:w-[32.158vw] w-[339.39px] aspect-square md:max-lg:w-[44.141vw]">
<motion.div
animate={{ rotate: 360 }}
transition={{ ease: "linear", duration: 50, repeat: Infinity }}
className="w-[23.611vw] aspect-[1/1] relative flex items-center justify-center "
className="lg:w-[32.158vw] md:w-[44.141vw] w-[339.39px] aspect-square relative flex items-center justify-center"
>
{Array.from({ length: 20 }).map((_, index) => (
<img src="/img/pages/about/team/3ds.png" alt="" className="" />
{/* {Array.from({ length: 20 }).map((_, index) => (
<div
key={index}
id="outer-team-circle"
@@ -70,7 +71,7 @@ function TeamCarousel() {
`}</style>
</div>
))}
</motion.div>
</motion.div> */}
</motion.div>
</div>
);
@@ -91,7 +92,7 @@ export default function AboutTeam() {
<span className="text-transparent bg-clip-text">
{" "}
более 60 высококвалифицирова
<br className="hidden max-md:block" />
<br className="max-md:block hidden" />
ных специалистов <br className="md:max-lg:hidden" />
</span>{" "}
в своем офисе в Екатеринбурге
@@ -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]"
>
Используем только собственные силы,{" "}
<br className="hidden md:max-lg:block" /> не передавая задачи на аутсорс
<br className="md:max-lg:block hidden" /> не передавая задачи на аутсорс
</span>
<div
@@ -165,7 +166,7 @@ export default function AboutTeam() {
transition={{ delay: 0.6, bounce: "none" }}
className="bg-gradient-to-r from-[#222324] to-[#171819] rounded-[1.111vw] p-[2.222vw] [grid-area:pm] flex items-end
md:max-lg:p-[4.167vw] md:max-lg:rounded-[2.083vw]
max-md:rounded-[4.444vw] max-md:p-[4.444vw]"
max-md:rounded-[4.444vw] max-md:p-[4.444vw] relative overflow-hidden"
>
<div className="mt-auto space-y-[1.667vw] max-md:space-y-[3.333vw]">
<span className="line1 md:max-lg:text-[12.5vw]">3</span>
@@ -212,12 +213,12 @@ export default function AboutTeam() {
</span>
<h3 className="headline2">
сотрудников в отделе <br /> 3D-моделирования, сколько{" "}
<br className="hidden max-md:block" />{" "}
<br className="hidden md:max-lg:block" /> точно,{" "}
<br className="max-md:block hidden" />{" "}
<br className="md:max-lg:block hidden" /> точно,{" "}
<br className="max-lg:hidden" /> никто не считал
</h3>
</div>
{/* <TeamCarousel /> */}
<TeamCarousel />
</motion.div>
</div>
@@ -17,7 +17,7 @@ export default function AchievementsCardsMobile({
) : (
<div
className={
"w-[15.486vw] aspect-[223/240] rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] md:max-lg:flex md:max-lg:top-0 md:max-lg:p-[2.083vw] max-md:p-[4.444vw] top-[8.333vw] left-0 max-lg:hidden relative w-full max-md:aspect-[165/240]"
"aspect-[223/240] rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] md:max-lg:flex md:max-lg:top-0 md:max-lg:p-[2.083vw] max-md:p-[4.444vw] top-[8.333vw] left-0 max-lg:hidden relative w-full max-md:aspect-[165/240]"
}
>
<div className="flex w-[12.361vw] h-[12.361vw] overflow-hidden rounded-full aspect-square md:max-lg:w-[17.448vw] md:max-lg:h-[17.448vw] md:max-lg:my-auto">
@@ -79,23 +79,22 @@ export default function Accordeon({
return (
<motion.div
ref={wrapperRef}
className="w-full bg-[#37393B99] backdrop-blur-[20px] rounded-[1.111vw] py-[1.667vw] px-[1.944vw]"
className="w-full bg-[#37393B99] backdrop-blur-[20px] rounded-[1.111vw] lg:p-[1.667vw] p-6"
onClick={() => onOpenChange(title)}
// layout // Enable layout animations
initial={{ height: `${closedElHeight}vw` }}
animate={{
height: opened ? `${openedElHeight}vw` : `${closedElHeight}vw`,
}}
transition={{ duration: 0.5, ease: "easeInOut" }}
>
<motion.div className="flex items-center justify-between hover:cursor-pointer">
<span className="text-[1.389vw] headline2">{title}</span>
<motion.div className="hover:cursor-pointer flex justify-between items-center">
<span className="headline2">{title}</span>
<motion.div
animate={{
background: opened ? "#7A7A7A" : "#313233",
}}
transition={{ duration: 0.5 }}
className="size-[2.222vw] border-[0.208vw] border-[#272829] bg-[#313233] rounded-full flex items-center justify-center"
className="lg:size-[2.222vw] lg:border-[0.208vw] border-[#272829] bg-[#313233] rounded-full flex items-center justify-center"
>
<div
style={{
@@ -112,7 +111,7 @@ export default function Accordeon({
{opened && (
<motion.ul
ref={listRef}
className="flex flex-col mt-[1.667vw] gap-[0.556vw]"
className="flex flex-col lg:mt-[1.667vw] lg:gap-[0.556vw]"
>
{content.map((item, index) => (
<motion.li
@@ -140,7 +139,7 @@ export default function Accordeon({
delay:
(content.length - 1 - index) * (0.125 / content.length),
duration: 0.2,
ease: "easeIn", // Smooth exit
ease: "easeIn",
},
}}
className="text2"
@@ -1,5 +1,5 @@
import { motion } from "framer-motion";
import React, { useEffect, useState } from "react";
import React from "react";
interface ControlBtnProps {
title: string;
@@ -7,7 +7,7 @@ interface ControlBtnProps {
active?: boolean;
progress?: number;
className?: string;
callback?: Function;
callback: () => void;
}
export default function WebHeroControlBtn({
@@ -20,7 +20,7 @@ export default function WebHeroControlBtn({
}: ControlBtnProps) {
return (
<motion.button
onClick={() => callback && callback()}
onClick={callback}
style={{
background: !active
? "linear-gradient(#37393B99 0%, #37393B99 100%)"
@@ -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 (
<motion.div
key={id}
initial={{
maxWidth: index === 1 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 1
? "48.125vw"
: "23.611vw"
: isMd
? index === 1
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
animate={{
maxWidth: index === 2 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 2
? "48.125vw"
: "23.611vw"
: isMd
? index === 2
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="p-[1.667vw] h-[37.361vw] relative flex items-center justify-center flex-shrink-0 snap-center w-full"
className="lg:h-[37.361vw] md:h-[538px] h-[480px] relative flex items-center justify-center flex-shrink-0 snap-center w-full select-none"
>
<AnimatePresence>
{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"
>
<span className="headline2 leading-[135%] tracking-[0] z-[3]">
Для соцсетей:
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60]">
<span className="headline2 z-[3]">Для соцсетей:</span>
<p className="text2 text-[#FFFFFF60]">
Вертикальные изображения с идеально выстоенным кадром
</p>
</motion.div>
@@ -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"
>
<span className="headline2 leading-[135%] tracking-[0]">
Наружной рекламы:
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60]">
<span className="headline2">Наружной рекламы:</span>
<p className="text2 text-[#FFFFFF60]">
Высокое разрешение для широкоформатной печати
</p>
</motion.div>
@@ -72,12 +85,18 @@ export default function ContentSlideCards({
<motion.img
key="card_top"
animate={{
translateX: active ? "-60px" : "-10px",
translateY: active ? "-60px" : "-10px",
width: active ? "22.986vw" : "15.833vw",
translateX: active ? "-3.819vw" : "-0.347vw",
translateY: active ? "-3.819vw" : "-0.347vw",
width: isLg
? active
? "23.063vw"
: "15.882vw"
: isMd
? "24.919vw"
: "191.38px",
}}
transition={{ duration: 0.2, ease: "easeInOut" }}
className=" aspect-[331/222] absolute z-[3]"
className="absolute z-[3] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_top.png"
alt=""
/>
@@ -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=""
/>
<motion.img
key="card_bt"
animate={{
translateX: active ? "60px" : "10px",
translateY: active ? "60px" : "10px",
width: active ? "22.986vw" : "15.833vw",
translateX: active ? "3.819vw" : "0.347vw",
translateY: active ? "3.819vw" : "0.347vw",
width: isLg
? active
? "23.063vw"
: "15.882vw"
: isMd
? "24.919vw"
: "191.38px",
}}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="w-[22.986vw] aspect-[331/222] absolute z-[1]"
className="absolute z-[1] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_bt.png"
alt=""
/>
@@ -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 (
<motion.div
key={id}
initial={{
maxWidth: index === 1 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 1
? "48.125vw"
: "23.611vw"
: isMd
? index === 1
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
animate={{
maxWidth: index === 2 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 2
? "48.125vw"
: "23.611vw"
: isMd
? index === 2
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="p-[1.667vw] h-[37.361vw] relative flex items-center justify-center flex-shrink-0 snap-center w-full"
className="lg:h-[37.361vw] md:h-[538px] h-[480px] relative flex items-center justify-center flex-shrink-0 snap-center w-full select-none"
>
<div ref={HallRef} className="absolute inset-0 w-full h-full z-[1]">
<motion.div
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 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] lg:rounded-[1.111vw] rounded-2xl absolute lg:left-[5.25vw] lg:bottom-[2.625vw] left-0 bottom-0"
>
<span className="headline2 leading-[135%] tracking-[0] z-[3]">
По двору:
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2 z-[3]">По двору:</span>
<p className="text2 text-[#FFFFFF60] ">
возможность ощутить ритм жизни Жилого комплекса и его атмосферу
</p>
</motion.div>
@@ -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"
>
<span className="headline2 leading-[135%] tracking-[0]">
По квартире:
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2">По квартире:</span>
<p className="text2 text-[#FFFFFF60] ">
возможность оценить пространство в объеме и ощутить себя внутри
</p>
</motion.div>
@@ -58,15 +70,31 @@ export default function ContentSlideHall({
<motion.div
animate={{
width: active ? "23.333vw" : "15.833vw",
height: active ? "23.333vw" : "15.833vw",
width: isLg
? active
? "23.333vw"
: "15.833vw"
: isMd
? active
? "29.688vw"
: "228px"
: "228px",
height: isLg
? active
? "23.333vw"
: "15.833vw"
: isMd
? active
? "29.688vw"
: "228px"
: "228px",
}}
className="relative rounded-full overflow-clip"
className="relative overflow-clip rounded-full"
>
<motion.img
src="/img/pages/web/content/hall.png"
alt=""
className="object-cover h-full"
className="aspect-square object-cover h-full"
/>
</motion.div>
</motion.div>
@@ -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 (
<motion.div
key={id}
initial={{
maxWidth: index === 1 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 1
? "48.125vw"
: "23.611vw"
: isMd
? index === 1
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
animate={{
maxWidth: index === 2 ? "48.125vw" : "23.611vw",
maxWidth: isLg
? index === 2
? "48.125vw"
: "23.611vw"
: isMd
? index === 2
? "54.688vw"
: "31.25vw"
: "94.444vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="p-[1.667vw] h-[37.361vw] relative flex-shrink-0 w-full"
className="lg:h-[37.361vw] md:h-[538px] h-[480px] relative flex-shrink-0 w-full select-none"
>
<div ref={IphoneRef} className="absolute inset-0 w-full h-full">
<div ref={IphoneRef} className="absolute inset-0">
<motion.div
animate={{
opacity: active ? 1 : 0,
top: active ? "1.528vw" : "0.528vw",
left: active ? "5.694vw" : "20.694vw",
top: isLg ? (active ? "1.528vw" : "0.528vw") : "0vw",
left: isLg ? (active ? "5.694vw" : "20.694vw") : "0vw",
}}
className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute"
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 justify-between bg-[#37393B99] backdrop-blur-[5px] lg:rounded-[1.111vw] rounded-2xl absolute"
>
<span className="headline2 leading-[135%] tracking-[0] z-[3]">
Яркие видео
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2 z-[3]">Яркие видео</span>
<p className="text2 text-[#FFFFFF60] ">
Дайте клиентам почувствовать атмосферу жизни в жилом комплексе
</p>
</motion.div>
@@ -46,15 +63,13 @@ export default function ContentSlideIphone({
<motion.div
animate={{
opacity: active ? 1 : 0,
top: active ? "16.736vw" : "8.736vw",
left: active ? "1.667vw" : "5.667vw",
top: isLg ? (active ? "15.736vw" : "8.736vw") : "100%",
left: isLg ? (active ? "1.667vw" : "5.667vw") : "100%",
}}
className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute"
className="max-lg:-translate-y-full max-lg:-translate-x-full lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:p-[1.389vw] p-4 lg:gap-[0.694vw] gap-2 bg-[#37393B99] backdrop-blur-[5px] lg:rounded-[1.111vw] rounded-2xl absolute"
>
<span className="headline2 leading-[135%] tracking-[0]">
Интерьеры
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2">Интерьеры</span>
<p className="text2 text-[#FFFFFF60] ">
Поможем выгодно показать клиентам ваши дизайнерские решения
</p>
</motion.div>
@@ -62,15 +77,19 @@ export default function ContentSlideIphone({
<motion.div
animate={{
opacity: active ? 1 : 0,
top: active ? "23.819vw" : "13.819vw",
left: active ? "9.306vw" : "20.306vw",
top: isLg ? (active ? "23.819vw" : "13.819vw") : "262.91px",
left: isLg
? active
? "9.306vw"
: "20.306vw"
: isMd
? "5.859vw"
: "5px",
}}
className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute"
className="lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:p-[1.389vw] p-4 lg:gap-[0.694vw] gap-2 bg-[#37393B99] backdrop-blur-[5px] lg:rounded-[1.111vw] rounded-2xl absolute"
>
<span className="headline2 leading-[135%] tracking-[0]">
Экстерьер
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2">Экстерьер</span>
<p className="text2 text-[#FFFFFF60] ">
Запечатлим лучшие места для досуга и отдыха в вашем проекте
</p>
</motion.div>
@@ -78,15 +97,13 @@ export default function ContentSlideIphone({
<motion.div
animate={{
opacity: active ? 1 : 0,
top: active ? "14.583vw" : "14.583vw",
right: active ? "1.736vw" : "5.583vw",
top: isLg ? (active ? "14.583vw" : "14.583vw") : "105.91px",
right: isLg ? (active ? "1.736vw" : "5.583vw") : "0px",
}}
className="w-[13.403vw] h-[8.472vw] flex flex-col p-[1.389vw] justify-between bg-[#37393B99] backdrop-blur-[5px] rounded-[1.111vw] absolute"
className="lg:w-[13.403vw] md:w-[18.229vw] w-[140px] flex flex-col lg:p-[1.389vw] p-4 lg:gap-[0.694vw] gap-2 bg-[#37393B99] backdrop-blur-[5px] lg:rounded-[1.111vw] rounded-2xl absolute"
>
<span className="headline2 leading-[135%] tracking-[0]">
Архитектура
</span>
<p className="text-[0.833vw] leading-[135%] tracking-[0] font-[400] text-[#FFFFFF60] ">
<span className="headline2">Архитектура</span>
<p className="text2 text-[#FFFFFF60] ">
Сделаем видеооблеты, чтобы показать клиентам жилой комплекс в
инфраструктуре города
</p>
@@ -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"
/>
</AnimatePresence>
</motion.div>
@@ -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 (
<div className=" -mx-[1.389vw]">
<div className="relative flex w-full overflow-hidden">
<div className="lg:-mx-[1.389vw] md:-mx-[2.083vw] -mx-[2.778vw]">
<div className="flex overflow-hidden relative w-full" {...handlers}>
<div
onTransitionEnd={() => 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() {
</div>
<div className="flex gap-[0.833vw] absolute left-1/2 bottom-0 -translate-x-1/2 z-10">
<button
className=" w-[3.333vw] h-[3.333vw] rounded-[1.111vw] text-white bg-gradient"
className="lg:rounded-[1.111vw] rounded-2xl text-white bg-gradient lg:p-[1.111vw] p-4"
onClick={() => moveSlide("left")}
>
<div className="size-[1.111vw] mx-auto">
<div className="lg:size-[1.111vw] size-4">
<ArrowLeftIcon />
</div>
</button>
<button
className=" w-[3.333vw] h-[3.333vw] rounded-[1.111vw] text-white bg-gradient"
className="lg:rounded-[1.111vw] rounded-2xl text-white bg-gradient lg:p-[1.111vw] p-4"
onClick={() => moveSlide("right")}
>
<div className="size-[1.111vw] mx-auto">
<div className="lg:size-[1.111vw] size-4">
<ArrowRightIcon />
</div>
</button>
+48 -63
View File
@@ -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<HTMLVideoElement>(null);
const [currentVideo, setCurrentVideo] = useState<number>(0);
const [autoPlay, setAutoPlay] = useState<boolean>(true);
const videoRef = useRef<HTMLVideoElement | null>(null);
const [playbackProgress, setPlaybackProgress] = useState(0);
useEffect(() => {
const updateProgress = useCallback(() => {
if (!videoRef.current) return;
videoRef.current.play();
}, [currentVideo]);
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]);
// Обработчик timeupdate напрямую в JSX через onTimeUpdate
const handleTimeUpdate = useCallback(() => {
updateProgress();
}, [updateProgress]);
const onVideoEnd = () => {
if (!autoPlay) return;
setCurrentVideo((cur) => (cur + 1) % 5);
};
// Сбрасываем прогресс при переключении видео
useEffect(() => {
setPlaybackProgress(0);
}, [currentVideo]);
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 (
<div>
<h1 className="text-center line1 leading-[85%] tracking-[-0.04em] font-medium md:max-lg:text-[6.25vw] md:max-lg:mb-[8.333vw] md:max-lg:leading-[95%] md:max-lg:tracking-[-0.04em]">
Создаем интерактивные сайты для продажи недвижимости
</h1>
<div className="relative flex justify-center md:max-lg:flex-col">
<div className="md:max-lg:flex-col flex relative justify-center">
<div className="relative">
<AnimatePresence mode="wait">
<motion.video
// key={currentVideo}
muted
key={currentVideo}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
ref={videoRef}
onEnded={onVideoEnd}
controls={false}
loop={!autoPlay}
onTimeUpdate={handleTimeUpdate}
onLoadStart={() => 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]"
/>
</AnimatePresence>
<img
draggable={false}
className="w-[63.75vw] aspect-[918/553] z-10 mt-[3.125vw] relative md:max-lg:w-full md:max-lg:mt-0"
@@ -85,56 +70,56 @@ export default function WebHero() {
/>
</div>
<div className="md:max-lg:flex md:max-lg:w-full md:max-lg:gap-[1.042vw] md:max-lg:mt-[4.167vw] z-[1]">
<div className="md:max-lg:flex md:max-lg:w-full md:max-lg:gap-[1.042vw] md:max-lg:mt-[4.167vw] z-10">
<WebHeroControlBtn
title={"Поиск"}
icon={<SearchIcon />}
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}
/>
<WebHeroControlBtn
title={"Поиск на генплане"}
icon={<GenPlanSearchIcon />}
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}
/>
<WebHeroControlBtn
title={"О проекте"}
icon={<InfoIcon />}
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}
/>
<WebHeroControlBtn
title={"Окружение"}
icon={<TreeIcon />}
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}
/>
<WebHeroControlBtn
title={"Избранное"}
icon={<FavoriteIcon />}
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}
/>
<WebHeroControlBtn
title={"Генерация и отправка КП"}
icon={<ReportIcon />}
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}
/>
</div>
</div>
+6
View File
@@ -0,0 +1,6 @@
{
"port": 3100,
"appPort": 3000,
"autoPlugins": true,
"plugins": []
}