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 { Feedback } from "@/components/Layout/Feedback";
import { Footer } from "@/components/Layout/Footer"; import { Footer } from "@/components/Layout/Footer";
import dynamic from "next/dynamic"; import { Header } from "@/components/Layout/Header";
import { PropsWithChildren } from "react"; import { PropsWithChildren } from "react";
export default function MainLayout({ children }: PropsWithChildren) { export default function MainLayout({ children }: PropsWithChildren) {
const Header = dynamic(
() => import("@/components/Layout/Header").then((mod) => mod.Header),
{ ssr: false }
);
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<Header /> <Header />
+22 -9
View File
@@ -2,7 +2,6 @@
"use client"; "use client";
import { api } from "@/api"; import { api } from "@/api";
import { useMediaQueries } from "@/hooks/useMediaQueries";
import { useScroll } from "@/hooks/useScroll"; import { useScroll } from "@/hooks/useScroll";
import { useCheckAuthQuery } from "@/queries/checkAuth"; import { useCheckAuthQuery } from "@/queries/checkAuth";
import { HeaderLink } from "@/ui/HeaderLink"; import { HeaderLink } from "@/ui/HeaderLink";
@@ -39,8 +38,6 @@ export function Header() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
const [productsOpened, setProductsOpened] = useState(false); const [productsOpened, setProductsOpened] = useState(false);
const { isLg, isXs, isSm, isMd } = useMediaQueries();
const pathname = usePathname(); const pathname = usePathname();
const burgerRef = useRef<HTMLDivElement>(null); const burgerRef = useRef<HTMLDivElement>(null);
@@ -60,8 +57,15 @@ export function Header() {
const scroll = useScroll(logoRef); const scroll = useScroll(logoRef);
const [headerWidth, setHeaderWidth] = useState<number>();
return ( 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 <Link
href={"/"} href={"/"}
ref={logoRef} ref={logoRef}
@@ -76,11 +80,15 @@ export function Header() {
<motion.nav <motion.nav
ref={navRef} ref={navRef}
animate={{ 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" 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 <Link
href={"/"} 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" 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> </div>
)} )}
<AnimatePresence> <AnimatePresence>
{burgerOpened && (isXs || isSm) && ( {burgerOpened && headerWidth && headerWidth < 768 && (
<> <>
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
@@ -203,14 +211,19 @@ export function Header() {
</motion.nav> </motion.nav>
</AnimatePresence> </AnimatePresence>
<AnimatePresence> <AnimatePresence>
{productsOpened && (isMd || isLg) && ( {productsOpened &&
headerWidth &&
(headerWidth >= 768 || headerWidth < 1440) && (
<> <>
<motion.div <motion.div
onClick={() => setProductsOpened(false)} onClick={() => setProductsOpened(false)}
ref={productsRef} ref={productsRef}
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ animate={{
width: isLg ? "max(68.889vw,360px)" : "calc(100vw - 32px)", width:
headerWidth && headerWidth >= 1440
? "max(68.889vw,360px)"
: "calc(100vw - 32px)",
opacity: 100, opacity: 100,
}} }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
@@ -1,74 +1,76 @@
import { useMediaQueries } from "@/hooks/useMediaQueries"; import { useMediaQueries } from "@/hooks/useMediaQueries";
import { motion } from "framer-motion"; import { motion, useScroll, useTransform } from "framer-motion";
import React, { useEffect, useRef, useState } from "react"; import React, { useRef } from "react";
export default function DisplacementCard({ export default function DisplacementCard({
children, children,
className, className,
index = 0,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
index?: number;
}) { }) {
const cardRef = useRef<HTMLDivElement>(null); const cardRef = useRef<HTMLDivElement>(null);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [cardTransform, setCardTransform] = useState({ x: 0, y: 0 });
const { isLg } = useMediaQueries(); const { isLg } = useMediaQueries();
useEffect(() => { // Получаем прогресс скролла относительно элемента карточки
const handleMouseMove = (e: MouseEvent) => { const { scrollYProgress } = useScroll({
setMousePosition({ x: e.clientX, y: e.clientY }); target: cardRef,
}; offset: ["start end", "end start"],
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,
}); });
} else {
setCardTransform({ x: 0, y: 0 }); // Создаем уникальные параметры для каждой карточки на основе её индекса
} const phaseX = index * 0.7; // Фазовое смещение для X
}, [isLg, mousePosition]); 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 ( return (
<motion.div <motion.div
animate={
isLg
? {
x: cardTransform.x,
y: cardTransform.y,
}
: {}
}
transition={{
type: "spring",
stiffness: 150,
damping: 15,
mass: 0.1,
}}
ref={cardRef} ref={cardRef}
style={{
x: x,
y: y,
rotate: rotate,
}}
className={ 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] ` + `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 className
@@ -215,7 +215,7 @@ export default function AboutExperience() {
</p> </p>
<DisplacementCardsWrapper> <DisplacementCardsWrapper>
<DisplacementCard className="lg:top-0 lg:left-[16.389vw]"> <DisplacementCard index={0} className="lg:top-0 lg:left-[16.389vw]">
<img <img
src={"/img/pages/about/experience/cards/table.png"} 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" 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> </p>
</DisplacementCard> </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 <img
src={"/img/pages/about/experience/cards/wow.png"} 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" 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> </p>
</DisplacementCard> </DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:left-[8.194vw]"> <DisplacementCard
index={2}
className="lg:bottom-0 lg:left-[8.194vw]"
>
<img <img
src={"/img/pages/about/experience/cards/sk.png"} 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" 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> </p>
</DisplacementCard> </DisplacementCard>
<DisplacementCard className="lg:top-0 lg:right-[8.194vw]"> <DisplacementCard index={3} className="lg:top-0 lg:right-[8.194vw]">
<img <img
src={"/img/pages/about/experience/cards/map.png"} 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" 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> </p>
</DisplacementCard> </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 <img
src={"/img/pages/about/experience/cards/dp.png"} 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 " 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> </p>
</DisplacementCard> </DisplacementCard>
<DisplacementCard className="lg:bottom-0 lg:right-[16.389vw]"> <DisplacementCard
index={5}
className="lg:bottom-0 lg:right-[16.389vw]"
>
<img <img
src={"/img/pages/about/experience/cards/build_up.png"} 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" 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"
+6 -15
View File
@@ -3,28 +3,19 @@ import React from "react";
export function AboutHero() { export function AboutHero() {
return ( return (
<div <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)]">
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] "
>
<video <video
loop loop
autoPlay autoPlay
playsInline playsInline
muted muted
src="/videos/pages/about/hero_video.mp4" 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 poster="/img/pages/about/reel_template.png"
max-lg:relative md:max-lg:aspect-[736/441] 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]"
max-md:aspect-[340/512] max-md:rounded-b-[4.444vw]" />
></video> <div className="bg-[#00000033] absolute inset-0 max-lg:hidden h-dvh z-[11]" />
<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"> <div className="relative z-[11] pt-[16.25vw] max-lg:pt-0 bg-clip-text">
<h1 <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]">
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 компания, GRAFF.estate <br className="max-md:block hidden" /> IT компания,
<br /> разрабатывающая <br className="md:max-lg:block hidden" />{" "} <br /> разрабатывающая <br className="md:max-lg:block hidden" />{" "}
передовые передовые
+1 -1
View File
@@ -1,9 +1,9 @@
"use client"; "use client";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { AboutHero } from "./AboutHero";
import { Statistics } from "../MainPage/Statistics"; import { Statistics } from "../MainPage/Statistics";
import AboutTeam from "./AboutTeam"; import AboutTeam from "./AboutTeam";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { AboutHero } from "./AboutHero";
export default function AboutMain() { export default function AboutMain() {
useEffect(() => { useEffect(() => {
@@ -22,7 +22,7 @@ export default function ContentSlideCards({
return ( return (
<motion.div <motion.div
key={id} // key={id}
initial={{ initial={{
maxWidth: isLg maxWidth: isLg
? index === 1 ? index === 1
@@ -46,7 +46,7 @@ export default function ContentSlideCards({
: "94.444vw", : "94.444vw",
}} }}
transition={{ duration: 0.5, 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" 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> <AnimatePresence>
{active && ( {active && (
@@ -55,20 +55,20 @@ export default function ContentSlideCards({
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} 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]" className="absolute inset-0 w-full h-full z-[4]"
> >
<motion.div <motion.div
initial={{ x: "4.167vw", y: "-4.167vw" }} initial={{ x: "4.167vw", y: "-4.167vw" }}
animate={{ x: 0, y: 0 }} animate={{ x: 0, y: 0 }}
exit={{ x: "4.167vw", y: "-4.167vw" }} 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 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]" 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> <span className="headline2 z-[3]">Для соцсетей:</span>
<p className="text2 text-[#FFFFFF60]"> <p className="text2 text-[#FFFFFF60]">
Вертикальные изображения с идеально выстоенным кадром Вертикальные изображения с идеально выстоенным кадром
</p> </p>
</motion.div> </motion.div>
@@ -76,7 +76,7 @@ export default function ContentSlideCards({
initial={{ x: "-4.167vw", y: "4.167vw" }} initial={{ x: "-4.167vw", y: "4.167vw" }}
animate={{ x: 0, y: 0 }} animate={{ x: 0, y: 0 }}
exit={{ x: "-4.167vw", y: "4.167vw" }} 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 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]" 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", translateY: active ? "-3.819vw" : "-0.347vw",
width: cardsSize, width: cardsSize,
}} }}
transition={{ duration: 0.2, ease: "easeInOut" }} transition={{ duration: 0.5, type: "just" }}
className="absolute z-[3] lg:rounded-[0.417vw] rounded-2xl" className="absolute z-[3] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_top.png" src="/img/pages/web/content/card_top.png"
alt="" alt=""
@@ -108,7 +108,7 @@ export default function ContentSlideCards({
translateY: "0px", translateY: "0px",
width: cardsSize, width: cardsSize,
}} }}
transition={{ duration: 0.4, ease: "easeInOut" }} transition={{ duration: 0.5, type: "just" }}
className="absolute z-[2] lg:rounded-[0.417vw] rounded-2xl" className="absolute z-[2] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_md.png" src="/img/pages/web/content/card_md.png"
alt="" alt=""
@@ -120,7 +120,7 @@ export default function ContentSlideCards({
translateY: active ? "3.819vw" : "0.347vw", translateY: active ? "3.819vw" : "0.347vw",
width: cardsSize, width: cardsSize,
}} }}
transition={{ duration: 0.5, ease: "easeInOut" }} transition={{ duration: 0.5, type: "just" }}
className="absolute z-[1] lg:rounded-[0.417vw] rounded-2xl" className="absolute z-[1] lg:rounded-[0.417vw] rounded-2xl"
src="/img/pages/web/content/card_bt.png" src="/img/pages/web/content/card_bt.png"
alt="" alt=""
@@ -42,7 +42,7 @@ export default function ContentSlideHall({
return ( return (
<motion.div <motion.div
key={id} // key={id}
initial={{ initial={{
maxWidth: isLg maxWidth: isLg
? index === 1 ? index === 1
@@ -65,7 +65,7 @@ export default function ContentSlideHall({
: "31.25vw" : "31.25vw"
: "94.444vw", : "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" 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 <div
@@ -112,7 +112,7 @@ export default function ContentSlideHall({
height: cardSize, height: cardSize,
backgroundPosition: `${backgroundPosition.x} ${backgroundPosition.y}`, 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)]" className="relative bg-red-500 rounded-full overflow-clip bg-no-repeat bg-[url(/img/pages/web/content/hall.png)]"
/> />
</motion.div> </motion.div>
@@ -78,9 +78,9 @@ export default function ContentSlideIphone({
: "94.444vw", : "94.444vw",
}} }}
transition={{ duration: 0.5, type: "just" }} 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 <motion.div
animate={{ animate={{
opacity: active ? 1 : 0, opacity: active ? 1 : 0,
@@ -90,12 +90,13 @@ export default function ContentSlideIphone({
"11.111vw", "11.111vw",
left: isLg ? (active ? "5.694vw" : "20.694vw") : "0vw", 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 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]" 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> <span className="headline2 z-[3] text-[4.444vw]">Яркие видео</span>
<p className="text2 text-[#FFFFFF60] "> <p className="text2 text-[#FFFFFF60] ">
Дайте клиентам почувствовать атмосферу жизни в жилом комплексе Дайте клиентам почувствовать атмосферу жизни в жилом комплексе
</p> </p>
</motion.div> </motion.div>
@@ -103,11 +104,12 @@ export default function ContentSlideIphone({
animate={{ animate={{
opacity: active ? 1 : 0, opacity: active ? 1 : 0,
top: top:
(isLg && (active ? "16.736vw" : "8.736vw")) || (isLg && (active ? "15.736vw" : "8.736vw")) ||
(isMd && (active ? "60.307vw" : "50.307vw")) || (isMd && (active ? "59.307vw" : "50.307vw")) ||
"134.722vw", "134.722vw",
left: isLg ? (active ? "1.667vw" : "5.667vw") : "100%", 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 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]" 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" ? "5.859vw"
: "5px", : "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 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]" 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> <span className="headline2">Экстерьер</span>
<p className="text2 text-[#FFFFFF60] "> <p className="text2 text-[#FFFFFF60] ">
Запечатлим лучшие места для досуга и отдыха в вашем проекте Запечатлим лучшие места для досуга и отдыха в вашем проекте
</p> </p>
</motion.div> </motion.div>
@@ -150,13 +153,14 @@ export default function ContentSlideIphone({
"40.278vw", "40.278vw",
right: isLg ? (active ? "1.736vw" : "5.583vw") : "0px", 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 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]" 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> <span className="headline2">Архитектура</span>
<p className="text2 text-[#FFFFFF60] "> <p className="text2 text-[#FFFFFF60] ">
Сделаем видеооблеты, чтобы показать клиентам жилой комплекс в Сделаем видеооблеты, чтобы показать клиентам жилой комплекс
инфраструктуре города в инфраструктуре города
</p> </p>
</motion.div> </motion.div>
</div> </div>
@@ -11,12 +11,13 @@ export default function InfiniteSlider() {
const { isLg, isMd } = useMediaQueries(); const { isLg, isMd } = useMediaQueries();
const offset = isLg ? 0 : isMd ? 10.677 : 95.833; const offset = isLg ? 0 : isMd ? 10.677 : 95.833;
const slideWidth = isLg const slideWidth = isLg
? 23.661 + 0.486 ? 23.611 + 0.5
: isMd : isMd
? 31.25 + 1.0415 ? 31.25 + 1.0416
: 94.444 + 1.389; : 94.444 + 1.389;
const [slideOffset, setSlideOffset] = useState(-slideWidth); const [slideOffset, setSlideOffset] = useState(-slideWidth);
const [isAnimating, setIsAnimating] = useState(false); const [isAnimating, setIsAnimating] = useState(false);
const [slides, setSlides] = useState([ const [slides, setSlides] = useState([
{ type: "hall", id: Math.random() }, { type: "hall", id: Math.random() },
{ type: "cards", id: Math.random() }, { type: "cards", id: Math.random() },
@@ -53,17 +54,14 @@ export default function InfiniteSlider() {
return ( return (
<div className="lg:-mx-[3vw] md:-mx-[2.083vw] -mx-[2.778vw]"> <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 <div
onTransitionEnd={() => setIsAnimating(false)} onTransitionEnd={() => setIsAnimating(false)}
className="flex lg:gap-[2vw] md:gap-[2.083vw] gap-[2.778vw]" className="flex lg:gap-[2vw] md:gap-[2.083vw] gap-[2.778vw]"
style={{ style={{
transform: `translateX(${slideOffset}vw)`, transform: `translateX(${slideOffset}vw)`,
transitionDuration: `${ transitionDuration: `${
slideOffset === -offset || slideOffset % (-2 * slideWidth) === -offset ? 0 : 0.5
slideOffset === -slideWidth * 2 - offset
? 0
: 0.5
}s`, }s`,
}} }}
> >
+47 -14
View File
@@ -8,6 +8,22 @@ import DisplacementCardsWrapper from "@/components/displacement/DisplacementCard
import DisplacementCard from "@/components/displacement/DisplacementCard"; import DisplacementCard from "@/components/displacement/DisplacementCard";
import { useMediaQuery } from "usehooks-ts"; 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() { export default function WebDemo() {
const [demoActive, setDemoActive] = useState(false); const [demoActive, setDemoActive] = useState(false);
const iframeRef = useRef<HTMLIFrameElement | null>(null); const iframeRef = useRef<HTMLIFrameElement | null>(null);
@@ -65,14 +81,14 @@ export default function WebDemo() {
</p> </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)]"> <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="md:max-lg:hidden" /> становится{" "}
<br className="hidden md:max-lg:block" /> <br className="md:max-lg:block hidden" />
частью живого <br className="max-lg:hidden" /> частью живого <br className="max-lg:hidden" />
города города
</h2> </h2>
<DisplacementCardsWrapper> <DisplacementCardsWrapper>
<DisplacementCard className="lg:top-0 lg:left-[16.389vw]"> <DisplacementCard index={0} className="lg:top-0 lg:left-[16.389vw]">
<div className="flex w-full h-full mix-blend-screen overflow-hidden aspect-square"> <div className="aspect-square flex overflow-hidden w-full h-full mix-blend-screen">
<video <video
src="/videos/pages/web/demo/people.mp4" src="/videos/pages/web/demo/people.mp4"
muted muted
@@ -88,7 +104,10 @@ export default function WebDemo() {
</p> </p>
</DisplacementCard> </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"> <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 <video
src="/videos/pages/web/demo/cars.mp4" src="/videos/pages/web/demo/cars.mp4"
@@ -105,15 +124,23 @@ export default function WebDemo() {
</p> </p>
</DisplacementCard> </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 <img
src={"/img/pages/web/demo/clouds.png"} src={"/img/pages/web/demo/clouds.png"}
className={ className="object-contain flex-1 scale-125"
"z-10 w-full my-auto group-hover:scale-110 transition-all duration-300 " +
"absolute top-0 left-0 w-full "
}
alt="Облака движутся в нужом направлении, как в реальном времени на вашей локации" alt="Облака движутся в нужом направлении, как в реальном времени на вашей локации"
/> />
<img
src={"/img/pages/web/demo/clouds.png"}
className="object-contain flex-1 scale-125"
alt="Облака движутся в нужом направлении, как в реальном времени на вашей локации"
/>
</div>
<p <p
className={ className={
"z-10 text-center btns md:max-lg:text-[1.563vw] max-md:text-[3.333vw] mt-auto" "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> </p>
</DisplacementCard> </DisplacementCard>
<DisplacementCard className="lg:top-0 lg:right-[8.194vw]"> <DisplacementCard index={3} className="lg:top-0 lg:right-[8.194vw]">
<div className="flex h-full w-full mix-blend-screen overflow-hidden aspect-square"> <div className="aspect-square flex overflow-hidden w-full h-full mix-blend-screen">
<video <video
src="/videos/pages/web/demo/building.mp4" src="/videos/pages/web/demo/building.mp4"
muted muted
@@ -141,7 +168,10 @@ export default function WebDemo() {
</p> </p>
</DisplacementCard> </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 <img
src={"/img/pages/web/demo/infrastructure.png"} src={"/img/pages/web/demo/infrastructure.png"}
className={ className={
@@ -158,7 +188,10 @@ export default function WebDemo() {
</p> </p>
</DisplacementCard> </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"> <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 <video
src="/videos/pages/web/demo/appartaments.mp4" 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]"> <div className="relative w-full h-full overflow-clip rounded-[1.111vw] md:max-lg:rounded-[2.083vw] max-md:rounded-[4.444vw]">
{!demoActive ? ( {!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]"> <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тур {`Попробуйте 3D\u2011тур
прямо сейчас`} прямо сейчас`}
</span> </span>
@@ -39,12 +39,12 @@ export default function WebExperience() {
max-md:rounded-[4.444vw] max-md:p-[8.889vw]" max-md:rounded-[4.444vw] max-md:p-[8.889vw]"
> >
<span className="z-[1] relative headline1"> <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" /> или с помощью фильтров <br className="max-lg:hidden" /> или с помощью фильтров
</span> </span>
<img <img
src="/img/pages/web/experience/genplan.png" 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="" alt=""
/> />
</motion.div> </motion.div>
@@ -113,8 +113,9 @@ export default function WebExperience() {
<img <img
src="/img/pages/web/experience/parking.png" src="/img/pages/web/experience/parking.png"
alt="" 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>
<motion.div <motion.div
animate={{ animate={{
@@ -130,12 +131,12 @@ export default function WebExperience() {
<img <img
src="/img/pages/web/experience/options.png" src="/img/pages/web/experience/options.png"
alt="" alt=""
className="hidden lg:block" className="lg:block hidden"
/> />
<img <img
src="/img/pages/web/experience/options_tablet.png" src="/img/pages/web/experience/options_tablet.png"
alt="" alt=""
className="hidden max-lg:block" className="max-lg:block hidden"
/> />
</motion.div> </motion.div>
</div> </div>
+5 -1
View File
@@ -1,12 +1,16 @@
"use client"; "use client";
import React from "react"; import React from "react";
import WebHero from "./WebHero"; import WebHero from "./WebHero";
import WebDemo from "./WebDemo"; // import WebDemo from "./WebDemo";
import WebExperience from "./WebExperience"; import WebExperience from "./WebExperience";
import WebDevelopmentTimeline from "./WebDevelopmentTimeline"; import WebDevelopmentTimeline from "./WebDevelopmentTimeline";
import FAQ from "./FAQ"; import FAQ from "./FAQ";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
const WebDemo = dynamic(() => import("./WebDemo").then((mod) => mod.default), {
ssr: false,
});
const InfiniteSlider = dynamic( const InfiniteSlider = dynamic(
() => () =>
import("./PageComponents/WebInfiniteSlider/WebInfiniteSlider").then( import("./PageComponents/WebInfiniteSlider/WebInfiniteSlider").then(