This commit is contained in:
2025-09-03 16:16:10 +05:00
parent ddbd5eb6b5
commit e94c62894d
14 changed files with 211 additions and 158 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

+1 -6
View File
@@ -1,14 +1,9 @@
import { Feedback } from "@/components/Layout/Feedback";
import { Footer } from "@/components/Layout/Footer";
import dynamic from "next/dynamic";
import { Header } from "@/components/Layout/Header";
import { PropsWithChildren } from "react";
export default function MainLayout({ children }: PropsWithChildren) {
const Header = dynamic(
() => import("@/components/Layout/Header").then((mod) => mod.Header),
{ ssr: false }
);
return (
<div className="flex flex-col">
<Header />
+22 -9
View File
@@ -2,7 +2,6 @@
"use client";
import { api } from "@/api";
import { useMediaQueries } from "@/hooks/useMediaQueries";
import { useScroll } from "@/hooks/useScroll";
import { useCheckAuthQuery } from "@/queries/checkAuth";
import { HeaderLink } from "@/ui/HeaderLink";
@@ -39,8 +38,6 @@ export function Header() {
const [burgerOpened, setBurgerOpened] = useState(false);
const [productsOpened, setProductsOpened] = useState(false);
const { isLg, isXs, isSm, isMd } = useMediaQueries();
const pathname = usePathname();
const burgerRef = useRef<HTMLDivElement>(null);
@@ -60,8 +57,15 @@ export function Header() {
const scroll = useScroll(logoRef);
const [headerWidth, setHeaderWidth] = useState<number>();
return (
<header className="lg:mt-[1.389vw] relative flex lg:px-[1.389vw] ">
<header
className="lg:mt-[1.389vw] relative flex lg:px-[1.389vw]"
ref={(el) => {
if (el) setHeaderWidth(el.clientWidth + 12);
}}
>
<Link
href={"/"}
ref={logoRef}
@@ -76,11 +80,15 @@ export function Header() {
<motion.nav
ref={navRef}
animate={{
width: burgerOpened && (isXs || isSm) ? 340 : "auto",
width:
burgerOpened && headerWidth && headerWidth < 768 ? 340 : "auto",
}}
className="fixed self-center max-lg:top-4 top-[1.389vw] lg:p-[0.278vw] p-1 lg:rounded-[1.389vw] rounded-[20px] bg-[#37393B99] [backdrop-filter:blur(40px)] lg:gap-[0.278vw] flex gap-1 z-[12] mx-auto left-1/2 -translate-x-1/2"
>
{((isLg && scroll < -logoRef.current?.clientHeight!) || !isLg) && (
{((headerWidth &&
headerWidth >= 1440 &&
scroll < -logoRef.current?.clientHeight!) ||
(headerWidth && headerWidth < 1440)) && (
<Link
href={"/"}
className="aspect-square lg:p-[1.111vw] p-3 hover:bg-[#232425] group active:bg-white lg:rounded-[1.111vw] rounded-2xl content-center m-auto"
@@ -156,7 +164,7 @@ export function Header() {
</div>
)}
<AnimatePresence>
{burgerOpened && (isXs || isSm) && (
{burgerOpened && headerWidth && headerWidth < 768 && (
<>
<motion.div
initial={{ opacity: 0 }}
@@ -203,14 +211,19 @@ export function Header() {
</motion.nav>
</AnimatePresence>
<AnimatePresence>
{productsOpened && (isMd || isLg) && (
{productsOpened &&
headerWidth &&
(headerWidth >= 768 || headerWidth < 1440) && (
<>
<motion.div
onClick={() => setProductsOpened(false)}
ref={productsRef}
initial={{ opacity: 0 }}
animate={{
width: isLg ? "max(68.889vw,360px)" : "calc(100vw - 32px)",
width:
headerWidth && headerWidth >= 1440
? "max(68.889vw,360px)"
: "calc(100vw - 32px)",
opacity: 100,
}}
exit={{ opacity: 0 }}
@@ -1,74 +1,76 @@
import { useMediaQueries } from "@/hooks/useMediaQueries";
import { motion } from "framer-motion";
import React, { useEffect, useRef, useState } from "react";
import { motion, useScroll, useTransform } from "framer-motion";
import React, { useRef } from "react";
export default function DisplacementCard({
children,
className,
index = 0,
}: {
children: React.ReactNode;
className?: string;
index?: number;
}) {
const cardRef = useRef<HTMLDivElement>(null);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [cardTransform, setCardTransform] = useState({ x: 0, y: 0 });
const { isLg } = useMediaQueries();
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
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,
// Получаем прогресс скролла относительно элемента карточки
const { scrollYProgress } = useScroll({
target: cardRef,
offset: ["start end", "end start"],
});
} else {
setCardTransform({ x: 0, y: 0 });
}
}, [isLg, mousePosition]);
// Создаем уникальные параметры для каждой карточки на основе её индекса
const phaseX = index * 0.7; // Фазовое смещение для X
const phaseY = index * 1.2; // Фазовое смещение для Y
const phaseRotate = index * 0.3; // Фазовое смещение для поворота
// Создаем трансформации для X и Y на основе скролла
// Используем функции для динамического вычисления значений
// Альтернативный подход с массивами значений
const x = useTransform(
scrollYProgress,
[0, 0.25, 0.5, 0.75, 1],
isLg
? [
0,
Math.sin(phaseX) * 8,
Math.sin(phaseX + Math.PI) * 8,
Math.sin(phaseX + Math.PI * 1.5) * 8,
0,
]
: [0, 0, 0, 0, 0]
);
const y = useTransform(
scrollYProgress,
[0, 0.25, 0.5, 0.75, 1],
isLg
? [
0,
Math.cos(phaseY) * 25,
Math.cos(phaseY + Math.PI) * 25,
Math.cos(phaseY + Math.PI * 1.5) * 25,
0,
]
: [0, 0, 0, 0, 0]
);
// Добавляем легкое вращение для более динамичного эффекта
const rotate = useTransform(
scrollYProgress,
[0, 0.5, 1],
isLg ? [0, Math.sin(phaseRotate) * 2, 0] : [0, 0, 0]
);
return (
<motion.div
animate={
isLg
? {
x: cardTransform.x,
y: cardTransform.y,
}
: {}
}
transition={{
type: "spring",
stiffness: 150,
damping: 15,
mass: 0.1,
}}
ref={cardRef}
style={{
x: x,
y: y,
rotate: rotate,
}}
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] lg:absolute max-lg:relative max-lg:w-full max-lg:h-full overflow-hidden md:max-lg:p-[2.083vw] max-md:p-[4.444vw] max-md:aspect-[165/240] ` +
className
@@ -215,7 +215,7 @@ export default function AboutExperience() {
</p>
<DisplacementCardsWrapper>
<DisplacementCard className="lg:top-0 lg:left-[16.389vw]">
<DisplacementCard index={0} className="lg:top-0 lg:left-[16.389vw]">
<img
src={"/img/pages/about/experience/cards/table.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[17.708vw] md:h-[17.708vw] w-[25.556vw] h-[25.556vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
@@ -229,7 +229,10 @@ export default function AboutExperience() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]">
<DisplacementCard
index={1}
className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]"
>
<img
src={"/img/pages/about/experience/cards/wow.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[17.708vw] md:h-[17.708vw] w-[33.333vw] h-[33.333vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
@@ -241,7 +244,10 @@ export default function AboutExperience() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:left-[8.194vw]">
<DisplacementCard
index={2}
className="lg:bottom-0 lg:left-[8.194vw]"
>
<img
src={"/img/pages/about/experience/cards/sk.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[25.708vw] md:h-[25.708vw] w-[45.556vw] h-[45.556vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
@@ -252,7 +258,7 @@ export default function AboutExperience() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:top-0 lg:right-[8.194vw]">
<DisplacementCard index={3} className="lg:top-0 lg:right-[8.194vw]">
<img
src={"/img/pages/about/experience/cards/map.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[20.708vw] md:h-[20.708vw] w-[35.556vw] h-[35.556vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
@@ -263,7 +269,10 @@ export default function AboutExperience() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-[7.708vw] lg:right-0 md:max-lg:!translate-y-[3.125vw]">
<DisplacementCard
index={4}
className="lg:bottom-[7.708vw] lg:right-0 md:max-lg:!translate-y-[3.125vw]"
>
<img
src={"/img/pages/about/experience/cards/dp.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[17.708vw] md:h-[17.708vw] w-[25.556vw] h-[25.556vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300 "
@@ -275,7 +284,10 @@ export default function AboutExperience() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:right-[16.389vw]">
<DisplacementCard
index={5}
className="lg:bottom-0 lg:right-[16.389vw]"
>
<img
src={"/img/pages/about/experience/cards/build_up.png"}
className="lg:w-[9.444vw] lg:h-[9.444vw] md:w-[17.708vw] md:h-[17.708vw] w-[25.556vw] h-[25.556vw] my-auto object-cover hover:scale-[1.15] transition-all duration-300"
+9 -18
View File
@@ -3,28 +3,19 @@ import React from "react";
export function AboutHero() {
return (
<div
className="w-[calc(100%+1.398vw*2)] -ml-[1.389vw] h-[calc(100dvh-13.333vw/4-1.489vw-100px)]m h-dvh -translate-y-[calc(13.333vw/4+1.489vw+100px)]
max-lg:flex max-lg:flex-col-reverse max-lg:h-auto max-lg:translate-y-0 max-lg:w-full max-lg:m-0 md:max-lg:gap-[10.026vw]
max-md:gap-[21.389vw] "
>
<div className="w-[calc(100%+1.398vw*2)] relative -ml-[1.389vw] h-dvh max-lg:flex max-lg:flex-col-reverse max-lg:h-auto max-lg:translate-y-0 max-lg:w-full max-lg:m-0 md:max-lg:gap-[10.026vw] max-md:gap-[21.389vw] lg:-mt-[calc(13.333vw/4+1.489vw+100px)]">
<video
loop
autoPlay
playsInline
muted
src="/videos/pages/about/hero_video.mp4"
className="absolute inset-0 object-cover w-full lg:h-dvh rounded-b-[1.111vw] z-[10] opacity-60
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" />
poster="/img/pages/about/reel_template.png"
className="absolute inset-0 object-cover w-full lg:h-dvh rounded-b-[1.111vw] z-[10] top-0 max-lg:relative md:max-lg:aspect-[736/441] max-md:aspect-[340/512] max-md:rounded-b-[4.444vw]"
/>
<div className="bg-[#00000033] absolute inset-0 max-lg:hidden h-dvh z-[11]" />
<div className="relative z-[11] pt-[16.25vw] max-lg:pt-0 bg-clip-text">
<h1
className="text-center z-[9] 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]"
>
<h1 className="text-center z-[9] 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="max-md:block hidden" /> IT компания,
<br /> разрабатывающая <br className="md:max-lg:block hidden" />{" "}
передовые
@@ -34,21 +25,21 @@ export function AboutHero() {
</h1>
<div
className={` z-[10] absolute bg-[radial-gradient(circle_at_top_right,#FF79D2_-200%,#C932E8_50%,#7A55FF_100%)] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] w-[4.653vw] h-[3.889vw] top-[14.567vw] left-[2.292vw] headline2
className={`z-[10] absolute bg-[radial-gradient(circle_at_top_right,#FF79D2_-200%,#C932E8_50%,#7A55FF_100%)] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] w-[4.653vw] h-[3.889vw] top-[14.567vw] left-[2.292vw] headline2
md:max-lg:w-[8.203vw] md:max-lg:h-[5.469vw] md:max-lg:rounded-[2.214vw] md:max-lg:left-[3.906vw] md:max-lg:top-[3.906vw] md:max-lg:p-[1.536vw] md:max-lg:px-[2.083vw]
max-md:w-[17.5vw] max-md:h-[11.667vw] max-md:rounded-[4.444vw] max-md:px-[3.333vw] max-md:py-[4.444vw] max-md:top-[6.667vw] max-md:left-0 max-md:leading-[2.444vw]`}
>
AI
</div>
<div
className={` z-[10] absolute bg-[#37393B99] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] border-[#484C54] border leading-6 w-[5.139vw] h-[3.889vw] top-[34.803vw] left-[20.411vw] headline2
className={`z-[10] absolute bg-[#37393B99] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] border-[#484C54] border leading-6 w-[5.139vw] h-[3.889vw] top-[34.803vw] left-[20.411vw] headline2
md:max-lg:w-[8.984vw] md:max-lg:h-[5.469vw] md:max-lg:rounded-[2.214vw] md:max-lg:left-[75.26vw] md:max-lg:top-[6vw] md:max-lg:p-[1.536vw] md:max-lg:px-[2.083vw]
max-md:w-[19.167vw] max-md:h-[11.667vw] max-md:rounded-[4.444vw] max-md:px-[3.333vw] max-md:py-[4.444vw] max-md:top-[44.444vw] max-md:left-[75.278vw] max-md:leading-[2.444vw]`}
>
VR
</div>
<div
className={` z-[10] absolute bg-[#37393B99] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] border-[#484C54] border leading-6 w-[20.556vw] h-[3.889vw] top-[39.917vw] left-[61.167vw] headline2
className={`z-[10] absolute bg-[#37393B99] backdrop-blur-[4px] text-center p-[1.111vw] rounded-[1.111vw] border-[#484C54] border leading-6 w-[20.556vw] h-[3.889vw] top-[39.917vw] left-[61.167vw] headline2
md:max-lg:w-[32.031vw] md:max-lg:h-[5.469vw] md:max-lg:rounded-[2.214vw] md:max-lg:left-[62.802vw] md:max-lg:top-[20vw] md:max-lg:p-[1.536vw] md:max-lg:px-[2.083vw]
max-md:w-[68.333vw] max-md:h-[11.667vw] max-md:rounded-[4.444vw] max-md:px-[3.333vw] max-md:py-[4.444vw] max-md:top-[65vw] max-md:left-[13.056vw] max-md:leading-[2.444vw]`}
>
+1 -1
View File
@@ -1,9 +1,9 @@
"use client";
import React, { useEffect } from "react";
import { AboutHero } from "./AboutHero";
import { Statistics } from "../MainPage/Statistics";
import AboutTeam from "./AboutTeam";
import dynamic from "next/dynamic";
import { AboutHero } from "./AboutHero";
export default function AboutMain() {
useEffect(() => {
@@ -22,7 +22,7 @@ export default function ContentSlideCards({
return (
<motion.div
key={id}
// key={id}
initial={{
maxWidth: isLg
? index === 1
@@ -46,7 +46,7 @@ export default function ContentSlideCards({
: "94.444vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="lg:h-[37.361vw] md:h-[68.448vw] h-[151.111vw] relative flex items-center justify-center flex-shrink-0 snap-center w-full select-none"
className="lg:h-[37.361vw] md:h-[68.448vw] h-[151.111vw] relative flex items-center justify-center flex-shrink-0 snap-center w-full select-none pointer-events-none"
>
<AnimatePresence>
{active && (
@@ -55,20 +55,20 @@ export default function ContentSlideCards({
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="absolute inset-0 w-full h-full z-[4]"
>
<motion.div
initial={{ x: "4.167vw", y: "-4.167vw" }}
animate={{ x: 0, y: 0 }}
exit={{ x: "4.167vw", y: "-4.167vw" }}
transition={{ duration: 0.6, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="lg:w-[13.403vw] md:w-[18.229vw] w-[38.889vw] 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
max-md:bottom-[15vw] max-md:left-[5vw] max-md:rounded-[4.444vw] md:max-lg:p-[2.083vw] md:max-lg:left-[2.083vw] md:max-lg:bottom-[2.083vw]"
>
<span className="headline2 z-[3]">Для соцсетей:</span>
<p className="text2 text-[#FFFFFF60]">
Вертикальные изображения с идеально выстоенным кадром
Вертикальные изображения с идеально выстоенным кадром
</p>
</motion.div>
@@ -76,7 +76,7 @@ export default function ContentSlideCards({
initial={{ x: "-4.167vw", y: "4.167vw" }}
animate={{ x: 0, y: 0 }}
exit={{ x: "-4.167vw", y: "4.167vw" }}
transition={{ duration: 0.6, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="lg:w-[13.403vw] md:w-[18.229vw] w-[38.889vw] 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
max-md:top-[15vw] max-md:right-[5vw] max-md:rounded-[4.444vw] md:max-lg:p-[2.083vw] md:max-lg:right-[2.083vw] md:max-lg:top-[2.083vw]"
>
@@ -96,7 +96,7 @@ export default function ContentSlideCards({
translateY: active ? "-3.819vw" : "-0.347vw",
width: cardsSize,
}}
transition={{ duration: 0.2, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="absolute z-[3] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_top.png"
alt=""
@@ -108,7 +108,7 @@ export default function ContentSlideCards({
translateY: "0px",
width: cardsSize,
}}
transition={{ duration: 0.4, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="absolute z-[2] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_md.png"
alt=""
@@ -120,7 +120,7 @@ export default function ContentSlideCards({
translateY: active ? "3.819vw" : "0.347vw",
width: cardsSize,
}}
transition={{ duration: 0.5, ease: "easeInOut" }}
transition={{ duration: 0.5, type: "just" }}
className="absolute z-[1] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_bt.png"
alt=""
@@ -42,7 +42,7 @@ export default function ContentSlideHall({
return (
<motion.div
key={id}
// key={id}
initial={{
maxWidth: isLg
? index === 1
@@ -65,7 +65,7 @@ export default function ContentSlideHall({
: "31.25vw"
: "94.444vw",
}}
transition={{ duration: 0.1, type: "just" }}
transition={{ duration: 0.5, type: "just" }}
className="lg:h-[37.361vw] md:h-[68.448vw] h-[151.111vw] relative flex items-center justify-center flex-shrink-0 snap-center w-full select-none"
>
<div
@@ -112,7 +112,7 @@ export default function ContentSlideHall({
height: cardSize,
backgroundPosition: `${backgroundPosition.x} ${backgroundPosition.y}`,
}}
transition={{ duration: 0.1, type: "just" }}
transition={{ duration: 0.3, type: "just" }}
className="relative bg-red-500 rounded-full overflow-clip bg-no-repeat bg-[url(/img/pages/web/content/hall.png)]"
/>
</motion.div>
@@ -78,9 +78,9 @@ export default function ContentSlideIphone({
: "94.444vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="lg:h-[37.361vw] md:h-[68.448vw] h-[151.111vw] relative flex-shrink-0 w-full select-none"
className="lg:h-[37.361vw] md:h-[68.448vw] h-[151.111vw] relative flex-shrink-0 w-full select-none pointer-events-none"
>
<div ref={IphoneRef} className="absolute inset-0 flex items-center">
<div ref={IphoneRef} className="flex absolute inset-0 items-center">
<motion.div
animate={{
opacity: active ? 1 : 0,
@@ -90,12 +90,13 @@ export default function ContentSlideIphone({
"11.111vw",
left: isLg ? (active ? "5.694vw" : "20.694vw") : "0vw",
}}
transition={{ duration: 0.5, type: "just" }}
className="lg:w-[13.403vw] z-[9] md:w-[18.229vw] w-[38.889vw] 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
md:max-lg:p-[2.083vw] max-md:rounded-[4.444vw] max-md:p-[4.444vw] max-md:w-[38.889vw]"
>
<span className="headline2 z-[3] text-[4.444vw]">Яркие видео</span>
<p className="text2 text-[#FFFFFF60] ">
Дайте клиентам почувствовать атмосферу жизни в жилом комплексе
Дайте клиентам почувствовать атмосферу жизни в жилом комплексе
</p>
</motion.div>
@@ -103,11 +104,12 @@ export default function ContentSlideIphone({
animate={{
opacity: active ? 1 : 0,
top:
(isLg && (active ? "16.736vw" : "8.736vw")) ||
(isMd && (active ? "60.307vw" : "50.307vw")) ||
(isLg && (active ? "15.736vw" : "8.736vw")) ||
(isMd && (active ? "59.307vw" : "50.307vw")) ||
"134.722vw",
left: isLg ? (active ? "1.667vw" : "5.667vw") : "100%",
}}
transition={{ duration: 0.5, type: "just" }}
className="max-lg:-translate-y-full z-[9] max-lg:-translate-x-full lg:w-[13.403vw] md:w-[18.229vw] w-[38.889vw] 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
max-md:rounded-[4.444vw] md:max-lg:p-[2.083vw] max-md:p-[4.444vw] max-md:w-[38.889vw]"
>
@@ -132,12 +134,13 @@ export default function ContentSlideIphone({
? "5.859vw"
: "5px",
}}
transition={{ duration: 0.5, type: "just" }}
className="lg:w-[13.403vw] z-[9] md:w-[18.229vw] w-[38.889vw] 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
md:max-lg:p-[2.083vw] max-md:rounded-[4.444vw] max-md:p-[4.444vw] max-md:w-[38.889vw]"
>
<span className="headline2">Экстерьер</span>
<p className="text2 text-[#FFFFFF60] ">
Запечатлим лучшие места для досуга и отдыха в вашем проекте
Запечатлим лучшие места для досуга и отдыха в вашем проекте
</p>
</motion.div>
@@ -150,13 +153,14 @@ export default function ContentSlideIphone({
"40.278vw",
right: isLg ? (active ? "1.736vw" : "5.583vw") : "0px",
}}
transition={{ duration: 0.5, type: "just" }}
className="lg:w-[13.403vw] z-[9] md:w-[18.229vw] w-[38.889vw] 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
md:max-lg:p-[2.083vw] max-md:rounded-[4.444vw] max-md:p-[4.444vw] max-md:w-[38.889vw]"
>
<span className="headline2">Архитектура</span>
<p className="text2 text-[#FFFFFF60] ">
Сделаем видеооблеты, чтобы показать клиентам жилой комплекс в
инфраструктуре города
Сделаем видеооблеты, чтобы показать клиентам жилой комплекс
в инфраструктуре города
</p>
</motion.div>
</div>
@@ -11,12 +11,13 @@ export default function InfiniteSlider() {
const { isLg, isMd } = useMediaQueries();
const offset = isLg ? 0 : isMd ? 10.677 : 95.833;
const slideWidth = isLg
? 23.661 + 0.486
? 23.611 + 0.5
: isMd
? 31.25 + 1.0415
? 31.25 + 1.0416
: 94.444 + 1.389;
const [slideOffset, setSlideOffset] = useState(-slideWidth);
const [isAnimating, setIsAnimating] = useState(false);
const [slides, setSlides] = useState([
{ type: "hall", id: Math.random() },
{ type: "cards", id: Math.random() },
@@ -53,17 +54,14 @@ export default function InfiniteSlider() {
return (
<div className="lg:-mx-[3vw] md:-mx-[2.083vw] -mx-[2.778vw]">
<div className="relative flex w-full overflow-hidden" {...handlers}>
<div className="flex overflow-hidden relative w-full" {...handlers}>
<div
onTransitionEnd={() => setIsAnimating(false)}
className="flex lg:gap-[2vw] md:gap-[2.083vw] gap-[2.778vw]"
style={{
transform: `translateX(${slideOffset}vw)`,
transitionDuration: `${
slideOffset === -offset ||
slideOffset === -slideWidth * 2 - offset
? 0
: 0.5
slideOffset % (-2 * slideWidth) === -offset ? 0 : 0.5
}s`,
}}
>
+47 -14
View File
@@ -8,6 +8,22 @@ import DisplacementCardsWrapper from "@/components/displacement/DisplacementCard
import DisplacementCard from "@/components/displacement/DisplacementCard";
import { useMediaQuery } from "usehooks-ts";
// CSS стили для анимации облаков
const cloudsAnimationStyles = `
@keyframes cloudMove {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
.cloud-animation {
animation: cloudMove 15s linear infinite;
}
`;
export default function WebDemo() {
const [demoActive, setDemoActive] = useState(false);
const iframeRef = useRef<HTMLIFrameElement | null>(null);
@@ -65,14 +81,14 @@ export default function WebDemo() {
</p>
<h2 className="line2 font-medium md:max-lg:text-[5.208vw] max-md:text-[8.889vw] max-md:mb-[calc(11.111vw-6.667vw)]">
Ваш жилой комплекс <br className="md:max-lg:hidden" /> становится{" "}
<br className="hidden md:max-lg:block" />
<br className="md:max-lg:block hidden" />
частью живого <br className="max-lg:hidden" />
города
</h2>
<DisplacementCardsWrapper>
<DisplacementCard className="lg:top-0 lg:left-[16.389vw]">
<div className="flex w-full h-full mix-blend-screen overflow-hidden aspect-square">
<DisplacementCard index={0} className="lg:top-0 lg:left-[16.389vw]">
<div className="aspect-square flex overflow-hidden w-full h-full mix-blend-screen">
<video
src="/videos/pages/web/demo/people.mp4"
muted
@@ -88,7 +104,10 @@ export default function WebDemo() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]">
<DisplacementCard
index={1}
className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]"
>
<div className="flex lg:w-[12.361vw] lg:h-[12.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden rounded-full aspect-square">
<video
src="/videos/pages/web/demo/cars.mp4"
@@ -105,15 +124,23 @@ export default function WebDemo() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:left-[8.194vw]">
<DisplacementCard
index={2}
className="lg:bottom-0 lg:left-[8.194vw]"
>
<style>{cloudsAnimationStyles}</style>
<div className="cloud-animation flex absolute top-0 h-full">
<img
src={"/img/pages/web/demo/clouds.png"}
className={
"z-10 w-full my-auto group-hover:scale-110 transition-all duration-300 " +
"absolute top-0 left-0 w-full "
}
className="object-contain flex-1 scale-125"
alt="Облака движутся в нужом направлении, как в реальном времени на вашей локации"
/>
<img
src={"/img/pages/web/demo/clouds.png"}
className="object-contain flex-1 scale-125"
alt="Облака движутся в нужом направлении, как в реальном времени на вашей локации"
/>
</div>
<p
className={
"z-10 text-center btns md:max-lg:text-[1.563vw] max-md:text-[3.333vw] mt-auto"
@@ -124,8 +151,8 @@ export default function WebDemo() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:top-0 lg:right-[8.194vw]">
<div className="flex h-full w-full mix-blend-screen overflow-hidden aspect-square">
<DisplacementCard index={3} className="lg:top-0 lg:right-[8.194vw]">
<div className="aspect-square flex overflow-hidden w-full h-full mix-blend-screen">
<video
src="/videos/pages/web/demo/building.mp4"
muted
@@ -141,7 +168,10 @@ export default function WebDemo() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-[7.708vw] lg:right-0 md:max-lg:!translate-y-[3.125vw]">
<DisplacementCard
index={4}
className="lg:bottom-[7.708vw] lg:right-0 md:max-lg:!translate-y-[3.125vw]"
>
<img
src={"/img/pages/web/demo/infrastructure.png"}
className={
@@ -158,7 +188,10 @@ export default function WebDemo() {
</p>
</DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:right-[16.389vw] ">
<DisplacementCard
index={5}
className="lg:bottom-0 lg:right-[16.389vw] "
>
<div className="flex lg:w-[12.361vw] lg:h-[12.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden rounded-full aspect-square">
<video
src="/videos/pages/web/demo/appartaments.mp4"
@@ -192,7 +225,7 @@ export default function WebDemo() {
<div className="relative w-full h-full overflow-clip rounded-[1.111vw] md:max-lg:rounded-[2.083vw] max-md:rounded-[4.444vw]">
{!demoActive ? (
<div className="absolute top-0 left-0 z-[8] w-full h-full bg-[#00000066] flex flex-col items-center justify-center max-md:p-[4.444vw]">
<span className="text-center whitespace-pre-line line2 font-medium">
<span className="line2 font-medium text-center whitespace-pre-line">
{`Попробуйте 3D\u2011тур
прямо сейчас`}
</span>
@@ -39,12 +39,12 @@ export default function WebExperience() {
max-md:rounded-[4.444vw] max-md:p-[8.889vw]"
>
<span className="z-[1] relative headline1">
Выбор квартир на <br className="hidden md:max-lg:block" /> генплане{" "}
Выбор квартир на <br className="md:max-lg:block hidden" /> генплане{" "}
<br className="max-lg:hidden" /> или с помощью фильтров
</span>
<img
src="/img/pages/web/experience/genplan.png"
className="absolute top-0 left-0 z-0 w-full md:max-lg:h-full"
className="md:max-lg:h-full absolute top-0 left-0 z-0 w-full"
alt=""
/>
</motion.div>
@@ -113,8 +113,9 @@ export default function WebExperience() {
<img
src="/img/pages/web/experience/parking.png"
alt=""
className="absolute top-0 left-0 z-0 h-full max-md:w-full max-md:h-auto"
className="max-md:w-full max-md:h-auto absolute top-0 left-0 z-0 h-full"
/>
<div className="absolute inset-0 bg-gradient-to-b from-[#00000099]" />
</motion.div>
<motion.div
animate={{
@@ -130,12 +131,12 @@ export default function WebExperience() {
<img
src="/img/pages/web/experience/options.png"
alt=""
className="hidden lg:block"
className="lg:block hidden"
/>
<img
src="/img/pages/web/experience/options_tablet.png"
alt=""
className="hidden max-lg:block"
className="max-lg:block hidden"
/>
</motion.div>
</div>
+5 -1
View File
@@ -1,12 +1,16 @@
"use client";
import React from "react";
import WebHero from "./WebHero";
import WebDemo from "./WebDemo";
// import WebDemo from "./WebDemo";
import WebExperience from "./WebExperience";
import WebDevelopmentTimeline from "./WebDevelopmentTimeline";
import FAQ from "./FAQ";
import dynamic from "next/dynamic";
const WebDemo = dynamic(() => import("./WebDemo").then((mod) => mod.default), {
ssr: false,
});
const InfiniteSlider = dynamic(
() =>
import("./PageComponents/WebInfiniteSlider/WebInfiniteSlider").then(