4 changed files with 197 additions and 142 deletions
@ -0,0 +1,40 @@ |
|||||||
|
function MainUtm() { |
||||||
|
return ( |
||||||
|
<section className="main-utm-section"> |
||||||
|
<div className="main-utm-inner"> |
||||||
|
<div className="main-utm-head"> |
||||||
|
<h2>UTM SYSTEM</h2> |
||||||
|
<p>드론 교통을 안전하게 관리하는 통합 관제 시스템</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="main-utm-flow"> |
||||||
|
<div className="utm-item"> |
||||||
|
<span className="num">01</span> |
||||||
|
<h3>비행 요청</h3> |
||||||
|
<p>운영자는 비행 계획을 등록합니다</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="utm-item"> |
||||||
|
<span className="num">02</span> |
||||||
|
<h3>자동 승인</h3> |
||||||
|
<p>공역과 규제 기준을 기반으로 승인됩니다</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="utm-item"> |
||||||
|
<span className="num">03</span> |
||||||
|
<h3>경로 관리</h3> |
||||||
|
<p>비행 경로를 실시간으로 추적합니다</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="utm-item"> |
||||||
|
<span className="num">04</span> |
||||||
|
<h3>실시간 관제</h3> |
||||||
|
<p>이상 상황을 즉시 감지하고 대응합니다</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default MainUtm; |
||||||
@ -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 ( |
||||||
|
<section className="main-section" ref={sectionRef}> |
||||||
|
<div className="main-bg-wrap"> |
||||||
|
<div className="main-bg" ref={bgRef}> |
||||||
|
<div className="main-bg-hero1"></div> |
||||||
|
|
||||||
|
<div className="main-bg-hero2" ref={hero2Ref}> |
||||||
|
<video ref={videoRef} className="hero-video" src="./images/test.mp4" muted loop playsInline preload="auto" /> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="main-visible-atmo main-visible-atmo1"></div> |
||||||
|
<div className="main-visible-atmo main-visible-atmo2"></div> |
||||||
|
<div className="main-visible-beam main-visible-beam1"></div> |
||||||
|
<div className="main-visible-beam main-visible-beam2"></div> |
||||||
|
<div className="main-visible-haze"></div> |
||||||
|
<div className="main-grid"></div> |
||||||
|
|
||||||
|
<div className="main-text"> |
||||||
|
<div className="text text-fill text-left-bottom" ref={text1Ref}> |
||||||
|
<span className="main-kicker">PAL NETWORKS</span> |
||||||
|
<span className="fill-line">Technology Partner for</span> |
||||||
|
<br /> |
||||||
|
<span className="fill-line">Advanced Air Mobility</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="text text-change text-center-hero" ref={text2Ref}> |
||||||
|
항공 데이터와 통합 관제 기술로 |
||||||
|
<br /> |
||||||
|
안전한 하늘길을 설계합니다 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="main-progress"> |
||||||
|
<span className="main-progress-bar" ref={progressBarRef}></span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="main-scroll-indicator"> |
||||||
|
<span className="main-scroll-line"></span> |
||||||
|
<span className="main-scroll-text">SCROLL</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default MainVisual; |
||||||
Loading…
Reference in new issue