upd
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 1.1 MiB |
@@ -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 />
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />{" "}
|
||||
передовые
|
||||
|
||||
@@ -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>
|
||||
|
||||
+12
-8
@@ -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`,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user