From 230a1366d2b6f871f4e7a766bd2ccd5e90765f8a Mon Sep 17 00:00:00 2001 From: geun <1416geun@naver.com> Date: Wed, 15 Apr 2026 15:26:44 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=81=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/main/MainUtm.jsx | 40 ++++++++ src/components/main/MainVisual.jsx | 145 +++++++++++++++++++++++++++++ src/css/common.css | 12 ++- src/pages/MainPage.jsx | 142 +--------------------------- 4 files changed, 197 insertions(+), 142 deletions(-) create mode 100644 src/components/main/MainUtm.jsx create mode 100644 src/components/main/MainVisual.jsx diff --git a/src/components/main/MainUtm.jsx b/src/components/main/MainUtm.jsx new file mode 100644 index 0000000..43cd08a --- /dev/null +++ b/src/components/main/MainUtm.jsx @@ -0,0 +1,40 @@ +function MainUtm() { + return ( + + + + UTM SYSTEM + 드론 교통을 안전하게 관리하는 통합 관제 시스템 + + + + + 01 + 비행 요청 + 운영자는 비행 계획을 등록합니다 + + + + 02 + 자동 승인 + 공역과 규제 기준을 기반으로 승인됩니다 + + + + 03 + 경로 관리 + 비행 경로를 실시간으로 추적합니다 + + + + 04 + 실시간 관제 + 이상 상황을 즉시 감지하고 대응합니다 + + + + + ); +} + +export default MainUtm; diff --git a/src/components/main/MainVisual.jsx b/src/components/main/MainVisual.jsx new file mode 100644 index 0000000..7bb1940 --- /dev/null +++ b/src/components/main/MainVisual.jsx @@ -0,0 +1,145 @@ +import { useEffect, useRef } from "react"; +import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +gsap.registerPlugin(ScrollTrigger); + +function MainVisual() { + const sectionRef = useRef(null); + const bgRef = useRef(null); + const hero2Ref = useRef(null); + const videoRef = useRef(null); + const text1Ref = useRef(null); + const text2Ref = useRef(null); + const progressBarRef = useRef(null); + + useEffect(() => { + const ctx = gsap.context(() => { + const videoEl = videoRef.current; + + if (videoEl) { + videoEl.pause(); + videoEl.currentTime = 0; + } + + gsap.set(hero2Ref.current, { opacity: 0, scale: 1.12, filter: "blur(12px)" }); + + gsap.set(text2Ref.current, { + opacity: 0, + y: 54, + x: 0, + scale: 0.965, + filter: "blur(10px)", + }); + + gsap.set(progressBarRef.current, { width: "0%" }); + gsap.set(".fill-line", { backgroundPosition: "100% 0%" }); + + const darkHeroTrigger = ScrollTrigger.create({ + trigger: sectionRef.current, + start: "top top", + end: "+=3000", + onEnter: () => document.body.classList.add("is-dark-hero"), + onEnterBack: () => document.body.classList.add("is-dark-hero"), + onLeave: () => document.body.classList.remove("is-dark-hero"), + onLeaveBack: () => document.body.classList.remove("is-dark-hero"), + }); + + let hero2Started = false; + + const tl = gsap.timeline({ + scrollTrigger: { + trigger: sectionRef.current, + start: "top top", + end: "+=3000", + scrub: 1.15, + pin: true, + anticipatePin: 1, + onUpdate: (self) => { + if (!videoEl) return; + + if (self.progress >= 0.42 && !hero2Started) { + hero2Started = true; + videoEl.currentTime = 0; + videoEl.play().catch(() => {}); + } + + if (self.progress < 0.42 && hero2Started) { + hero2Started = false; + videoEl.pause(); + videoEl.currentTime = 0; + } + }, + }, + }); + + tl.to(bgRef.current, { width: "100vw", height: "100vh", borderRadius: 0, ease: "none" }, 0) + .to(bgRef.current, { scale: 1.08, ease: "none" }, 0) + .to(progressBarRef.current, { width: "100%", ease: "none" }, 0) + .to(".fill-line", { backgroundPosition: "0% 0%", ease: "none", stagger: 0.14, duration: 1.35 }, 0.1) + + .to(hero2Ref.current, { opacity: 1, scale: 1.035, filter: "blur(0px)", ease: "none" }, 1.55) + .to(text1Ref.current, { opacity: 0, y: -34, scale: 0.985, filter: "blur(8px)", ease: "none" }, 1.78) + .to(text2Ref.current, { opacity: 1, y: 0, scale: 1, filter: "blur(0px)", ease: "none" }, 1.95) + .to(hero2Ref.current, { scale: 1.095, ease: "none" }, 2.5); + + return () => { + if (videoEl) { + videoEl.pause(); + videoEl.currentTime = 0; + } + darkHeroTrigger.kill(); + document.body.classList.remove("is-dark-hero"); + }; + }, sectionRef); + + return () => ctx.revert(); + }, []); + + return ( + + + + + + + + + + + + + + + + + + + PAL NETWORKS + Technology Partner for + + Advanced Air Mobility + + + + 항공 데이터와 통합 관제 기술로 + + 안전한 하늘길을 설계합니다 + + + + + + + + + + SCROLL + + + + + ); +} + +export default MainVisual; diff --git a/src/css/common.css b/src/css/common.css index 8e7d80f..b3b6336 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -20,12 +20,13 @@ body{overflow-x:hidden;} .main-page{width:100%;overflow:hidden;} .main-section{position:relative;height:100vh;} .main-bg-wrap{position:relative;width:100%;height:100vh;display:flex;align-items:flex-start;justify-content:center;overflow:hidden;} -.main-bg{position:relative;width:min(1440px,calc(100vw - 80px));height:600px;border-radius:24px;overflow:hidden;transform-origin:center center;will-change:transform,width,height,border-radius;background:#050b17;} +.main-bg{position:relative;width:min(1440px,calc(100vw - 80px));height:750px;border-radius:24px;overflow:hidden;transform-origin:center center;will-change:transform,width,height,border-radius;background:#050b17;} .main-bg-hero1{position:absolute;inset:0;background:url('/images/hero1.png') no-repeat 50% 50%/cover;transform:scale(1.04);} -.main-bg-hero2{position:absolute;inset:0;opacity:0;overflow:hidden;will-change:transform,opacity,filter;} -.main-bg-hero2::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(3,8,20,.18) 0%,rgba(3,8,20,.28) 35%,rgba(3,8,20,.42) 100%),rgba(0,0,0,.18);pointer-events:none;} -.hero-video{width:100%;height:100%;object-fit:cover;filter:brightness(.82) contrast(1.06) saturate(.96);transform:scale(1.02);} +/* .main-bg-hero2{position:absolute;inset:0;background:url('/images/hero2.png') no-repeat 50% 50%/cover;opacity:0;} */ +.main-bg-hero2{position:absolute;inset:0;opacity:0;overflow:hidden;} +.hero-video{width:100%;height:100%;object-fit:cover;} +.main-bg-hero3{position:absolute;inset:0;background:url('/images/hero3.png') no-repeat 50% 50%/cover;opacity:0;} .main-bg::before{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(4,10,24,.12) 0%,rgba(4,10,24,.24) 38%,rgba(4,10,24,.56) 100%),linear-gradient(90deg,rgba(0,0,0,.16) 0%,rgba(0,0,0,.05) 42%,rgba(0,0,0,.12) 100%);z-index:1;pointer-events:none;} .main-bg::after{content:"";position:absolute;inset:0;background:radial-gradient(circle at 50% 58%,rgba(255,255,255,.06) 0%,rgba(255,255,255,.024) 18%,rgba(255,255,255,0) 44%),radial-gradient(circle at 50% 78%,rgba(58,64,129,.16) 0%,rgba(58,64,129,.08) 20%,rgba(58,64,129,0) 44%);z-index:1;pointer-events:none;} @@ -43,14 +44,17 @@ body{overflow-x:hidden;} .main-grid{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:.18;background-image:linear-gradient(rgba(174,206,255,.08) 1px,transparent 1px),linear-gradient(90deg,rgba(174,206,255,.08) 1px,transparent 1px);background-size:64px 64px;mask-image:linear-gradient(180deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.22) 20%,rgba(0,0,0,.72) 52%,rgba(0,0,0,.18) 100%);animation:gridShift 10s linear infinite;} .main-text{position:absolute;inset:0;z-index:3;pointer-events:none;} + .main-text .text{position:absolute;color:#fff;text-shadow:0 10px 30px rgba(0,0,0,.22);will-change:transform,opacity,filter;} .text-left-bottom{left:92px;bottom:124px;width:100%;max-width:760px;text-align:left;font-size:64px;line-height:1.12;font-weight:700;letter-spacing:-.05em;} + .text-center-hero{left:50%;top:45%;transform:translate(-50%,-50%);width:min(1080px,calc(100% - 120px));text-align:center;font-size:56px;line-height:1.24;font-weight:700;letter-spacing:-.045em;} .main-text .text-change{opacity:0;} .main-kicker{display:block;margin-bottom:18px;font-size:12px;letter-spacing:.22em;font-weight:600;color:rgba(255,255,255,.68);} + .main-text .text-fill .fill-line{display:inline-block;color:rgba(255,255,255,.28);background:linear-gradient(90deg,#fff 0%,#fff 50%,rgba(255,255,255,.28) 50%,rgba(255,255,255,.28) 100%);background-size:220% 100%;background-position:100% 0;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;} .main-progress{position:absolute;left:50%;bottom:42px;transform:translateX(-50%);width:min(240px,calc(100% - 48px));height:2px;background:rgba(255,255,255,.22);z-index:3;overflow:hidden;border-radius:999px;} diff --git a/src/pages/MainPage.jsx b/src/pages/MainPage.jsx index 3cdcc43..7f0820b 100644 --- a/src/pages/MainPage.jsx +++ b/src/pages/MainPage.jsx @@ -1,145 +1,11 @@ -import { useEffect, useRef } from "react"; -import { gsap } from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; - -gsap.registerPlugin(ScrollTrigger); +import MainVisual from "../components/main/MainVisual"; +import MainUtm from "../components/main/MainUtm"; function MainPage() { - const sectionRef = useRef(null); - const bgRef = useRef(null); - const hero2Ref = useRef(null); - const videoRef = useRef(null); - const text1Ref = useRef(null); - const text2Ref = useRef(null); - const progressBarRef = useRef(null); - - useEffect(() => { - const ctx = gsap.context(() => { - const videoEl = videoRef.current; - - if (videoEl) { - videoEl.pause(); - videoEl.currentTime = 0; - } - - gsap.set(hero2Ref.current, { opacity: 0, scale: 1.12, filter: "blur(12px)" }); - - gsap.set(text2Ref.current, { - opacity: 0, - y: 54, - x: 0, - scale: 0.965, - filter: "blur(10px)", - }); - - gsap.set(progressBarRef.current, { width: "0%" }); - gsap.set(".fill-line", { backgroundPosition: "100% 0%" }); - - const darkHeroTrigger = ScrollTrigger.create({ - trigger: sectionRef.current, - start: "top top", - end: "+=3000", - onEnter: () => document.body.classList.add("is-dark-hero"), - onEnterBack: () => document.body.classList.add("is-dark-hero"), - onLeave: () => document.body.classList.remove("is-dark-hero"), - onLeaveBack: () => document.body.classList.remove("is-dark-hero"), - }); - - let hero2Started = false; - - const tl = gsap.timeline({ - scrollTrigger: { - trigger: sectionRef.current, - start: "top top", - end: "+=3000", - scrub: 1.15, - pin: true, - anticipatePin: 1, - onUpdate: (self) => { - if (!videoEl) return; - - if (self.progress >= 0.42 && !hero2Started) { - hero2Started = true; - videoEl.currentTime = 0; - videoEl.play().catch(() => {}); - } - - if (self.progress < 0.42 && hero2Started) { - hero2Started = false; - videoEl.pause(); - videoEl.currentTime = 0; - } - }, - }, - }); - - tl.to(bgRef.current, { width: "100vw", height: "100vh", borderRadius: 0, ease: "none" }, 0) - .to(bgRef.current, { scale: 1.08, ease: "none" }, 0) - .to(progressBarRef.current, { width: "100%", ease: "none" }, 0) - .to(".fill-line", { backgroundPosition: "0% 0%", ease: "none", stagger: 0.14, duration: 1.35 }, 0.1) - - .to(hero2Ref.current, { opacity: 1, scale: 1.035, filter: "blur(0px)", ease: "none" }, 1.55) - .to(text1Ref.current, { opacity: 0, y: -34, scale: 0.985, filter: "blur(8px)", ease: "none" }, 1.78) - .to(text2Ref.current, { opacity: 1, y: 0, scale: 1, filter: "blur(0px)", ease: "none" }, 1.95) - .to(hero2Ref.current, { scale: 1.095, ease: "none" }, 2.5); - - return () => { - if (videoEl) { - videoEl.pause(); - videoEl.currentTime = 0; - } - darkHeroTrigger.kill(); - document.body.classList.remove("is-dark-hero"); - }; - }, sectionRef); - - return () => ctx.revert(); - }, []); - return ( - - - - - - - - - - - - - - - - - - - PAL NETWORKS - Technology Partner for - - Advanced Air Mobility - - - - 항공 데이터와 통합 관제 기술로 - - 안전한 하늘길을 설계합니다 - - - - - - - - - - SCROLL - - - - + + ); }
드론 교통을 안전하게 관리하는 통합 관제 시스템
운영자는 비행 계획을 등록합니다
공역과 규제 기준을 기반으로 승인됩니다
비행 경로를 실시간으로 추적합니다
이상 상황을 즉시 감지하고 대응합니다