You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
624 lines
22 KiB
624 lines
22 KiB
import { useRef } from "react"; |
|
import SubHero from "../../components/SubHero"; |
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
import { motion, useInView, useScroll, useTransform } from "framer-motion"; |
|
import { |
|
MapPin, |
|
ShieldAlert, |
|
Network, |
|
BadgeCheck, |
|
Cloud, |
|
FileCheck, |
|
Database, |
|
Layers, |
|
Navigation, |
|
CheckCircle, |
|
Radio, |
|
Plane, |
|
Bell, |
|
} from "lucide-react"; |
|
|
|
const ease = [0.22, 1, 0.36, 1]; |
|
const basePath = import.meta.env.BASE_URL; |
|
|
|
const INTRO_CARDS = [ |
|
{ label: "비행 계획", icon: Navigation }, |
|
{ label: "비행 승인", icon: CheckCircle }, |
|
{ label: "실시간 관제", icon: Radio }, |
|
{ label: "데이터 관리", icon: Database }, |
|
]; |
|
const UTM_WHAT_LEFT = [ |
|
{ icon: MapPin, label: "실시간 위치 감시" }, |
|
{ icon: ShieldAlert, label: "충돌 위험 사전 분석" }, |
|
{ icon: Cloud, label: "기상 정보 자동 연계" }, |
|
{ icon: Network, label: "공역 설정·지오펜싱" }, |
|
]; |
|
|
|
const UTM_WHAT_RIGHT = [ |
|
{ icon: FileCheck, label: "비행 계획 및 경로 승인" }, |
|
{ icon: BadgeCheck, label: "불법 기체 실시간 감지" }, |
|
{ icon: Database, label: "비행 데이터 통합 관리" }, |
|
{ icon: Layers, label: "UAM·드론 시스템 연동" }, |
|
]; |
|
|
|
const EVO_ITEMS = [ |
|
{ |
|
img: `${basePath}images/utm_intro_drone.png`, |
|
alt: "DRONE", |
|
name: "DRONE", |
|
desc: ( |
|
<> |
|
개별 드론 운용 단계. |
|
<br /> |
|
단순 비행 임무 수행 중심으로 체계적 관리 체계 부재. |
|
</> |
|
), |
|
tag: "단일 기체 운용", |
|
side: "left", |
|
active: false, |
|
}, |
|
{ |
|
img: `${basePath}images/utm_intro_utm.png`, |
|
alt: "UTM", |
|
name: "UTM", |
|
desc: ( |
|
<> |
|
무인기 교통관리 시스템 도입. |
|
<br /> |
|
비행 경로 승인·충돌 방지·공역 관리 체계화. |
|
</> |
|
), |
|
tag: "공역 관리 시스템", |
|
side: "right", |
|
active: false, |
|
}, |
|
{ |
|
img: `${basePath}images/utm_intro_uam.png`, |
|
alt: "UAM", |
|
name: "UAM", |
|
desc: ( |
|
<> |
|
도심항공모빌리티 등장. |
|
<br /> |
|
유인 eVTOL 기체가 도심 저고도 공역을 비행. |
|
</> |
|
), |
|
tag: "도심 항공 모빌리티", |
|
side: "left", |
|
active: false, |
|
}, |
|
{ |
|
img: `${basePath}images/utm_intro_uatm.png`, |
|
alt: "UATM", |
|
name: "UATM", |
|
desc: ( |
|
<> |
|
UAM과 UTM의 완전한 통합. |
|
<br /> |
|
기체 운용부터 관제까지 하나의 플랫폼으로 실현. |
|
</> |
|
), |
|
tag: "통합 관제 플랫폼", |
|
side: "right", |
|
active: true, |
|
}, |
|
]; |
|
|
|
const SECTORS = [ |
|
{ |
|
id: 0, |
|
num: "01", |
|
name: "UAM 기체 운용", |
|
color: "#E8193C", |
|
tags: [ |
|
"eVTOL 기체 직접 운용", |
|
"실 운용 데이터 기반 검증", |
|
"국내 유일 통합 사업자", |
|
"안전 관리 체계 구축", |
|
], |
|
}, |
|
{ |
|
id: 1, |
|
num: "02", |
|
name: "UTM 관제 기술", |
|
color: "#1A1F5E", |
|
tags: [ |
|
"실시간 공역 감시·관제", |
|
"충돌 위험 사전 분석", |
|
"비행 경로 승인 시스템", |
|
"지오펜싱·공역 설정", |
|
], |
|
}, |
|
{ |
|
id: 2, |
|
num: "03", |
|
name: "통합 항공 데이터 플랫폼", |
|
color: "#3D3D3D", |
|
tags: [ |
|
"비행 데이터 수집·분석", |
|
"UAM·드론 시스템 연동", |
|
"기상 정보 자동 연계", |
|
"통합 데이터 관리", |
|
], |
|
}, |
|
]; |
|
|
|
/* ──────────────────────────────────────── |
|
EvoItem |
|
──────────────────────────────────────── */ |
|
function EvoItem({ item, index }) { |
|
const itemRef = useRef(null); |
|
const itemInView = useInView(itemRef, { once: true, margin: "-80px" }); |
|
const itemActive = useInView(itemRef, { margin: "-45% 0px -45% 0px" }); |
|
|
|
return ( |
|
<motion.div |
|
ref={itemRef} |
|
className={`utm-evo__item${item.active ? " utm-evo__item--active" : ""}${itemActive ? " utm-evo__item--highlight" : ""}`} |
|
initial={{ opacity: 0, y: 40 }} |
|
animate={itemInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, ease }} |
|
> |
|
{item.side === "left" ? ( |
|
<> |
|
<div className="utm-evo__card-wrap utm-evo__card-wrap--left"> |
|
<div className="utm-evo__card"> |
|
<img |
|
src={item.img} |
|
alt={item.alt} |
|
className="utm-evo__card-img" |
|
/> |
|
<div className="utm-evo__card-body"> |
|
<div className="utm-evo__card-name">{item.name}</div> |
|
<div className="utm-evo__card-desc">{item.desc}</div> |
|
<span className="utm-evo__card-tag">{item.tag}</span> |
|
</div> |
|
</div> |
|
</div> |
|
<div className="utm-evo__dot-wrap"> |
|
<div className="utm-evo__dot"> |
|
<span>{String(index + 1).padStart(2, "0")}</span> |
|
</div> |
|
</div> |
|
<div className="utm-evo__empty" /> |
|
</> |
|
) : ( |
|
<> |
|
<div className="utm-evo__empty" /> |
|
<div className="utm-evo__dot-wrap"> |
|
<div className="utm-evo__dot"> |
|
<span>{String(index + 1).padStart(2, "0")}</span> |
|
</div> |
|
</div> |
|
<div className="utm-evo__card-wrap utm-evo__card-wrap--right"> |
|
<div className="utm-evo__card"> |
|
<img |
|
src={item.img} |
|
alt={item.alt} |
|
className="utm-evo__card-img" |
|
/> |
|
<div className="utm-evo__card-body"> |
|
<div className="utm-evo__card-name">{item.name}</div> |
|
<div className="utm-evo__card-desc">{item.desc}</div> |
|
<span className="utm-evo__card-tag">{item.tag}</span> |
|
</div> |
|
</div> |
|
</div> |
|
</> |
|
)} |
|
</motion.div> |
|
); |
|
} |
|
|
|
/* ──────────────────────────────────────── |
|
IntroPage |
|
──────────────────────────────────────── */ |
|
function IntroPage() { |
|
const ref = useFadeIn(); |
|
const UTM_NAV = [ |
|
{ label: "UTM/UATM 소개", to: "/utm/intro" }, |
|
{ label: "도입사례", to: "/utm/case" }, |
|
]; |
|
|
|
const introRef = useRef(null); |
|
const introInView = useInView(introRef, { once: true, margin: "-60px" }); |
|
|
|
const whatRef = useRef(null); |
|
const whatInView = useInView(whatRef, { once: true, margin: "-100px" }); |
|
|
|
const evoRef = useRef(null); |
|
const evoInView = useInView(evoRef, { once: true, margin: "-100px" }); |
|
const evoTrackRef = useRef(null); |
|
|
|
const capRef = useRef(null); |
|
const capInView = useInView(capRef, { once: true, margin: "-80px" }); |
|
|
|
const { scrollYProgress } = useScroll({ |
|
target: evoTrackRef, |
|
offset: ["start center", "end center"], |
|
}); |
|
const lineHeight = useTransform(scrollYProgress, [0, 1], ["0%", "100%"]); |
|
|
|
return ( |
|
<article ref={ref}> |
|
<SubHero |
|
label="UTM/UATM" |
|
title={ |
|
<> |
|
<em>UTM/UATM</em> |
|
</> |
|
} |
|
navItems={UTM_NAV} |
|
/> |
|
|
|
<div className="sub-content"> |
|
{/* ── 01 Overview ── */} |
|
<div className="inner-wrap"> |
|
<section className="utm-intro" ref={introRef}> |
|
<div className="utm-intro__top"> |
|
<motion.span |
|
className="fc-eyebrow" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={introInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, ease }} |
|
> |
|
Overview |
|
</motion.span> |
|
<motion.h2 |
|
className="utm-intro__title" |
|
initial={{ opacity: 0, y: 20 }} |
|
animate={introInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.1, ease }} |
|
> |
|
PAL Networks의 UTM/UATM은 |
|
<br /> |
|
<em>도심 항공 관제의 {""}</em> |
|
<em>새로운 기준입니다.</em> |
|
</motion.h2> |
|
<motion.p |
|
className="utm-intro__desc" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={introInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, delay: 0.2, ease }} |
|
> |
|
UAM 기체 운용과 UTM 관제 플랫폼을 동시에 보유한 국내 유일의 통합 |
|
사업자입니다. |
|
<br />실 운용 데이터로 검증된 시스템으로{" "} |
|
<strong>안전한 저고도 공역 관리</strong>를 실현합니다. |
|
</motion.p> |
|
</div> |
|
|
|
<ul className="utm-intro__cards"> |
|
{INTRO_CARDS.map((card, i) => ( |
|
<motion.li |
|
key={card.label} |
|
className="utm-intro__card" |
|
initial={{ opacity: 0, y: 60 }} |
|
animate={introInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.8, delay: 0.2 + i * 0.12, ease }} |
|
> |
|
<span className="utm-intro__card-num"> |
|
{String(i + 1).padStart(2, "0")} |
|
</span> |
|
<span className="utm-intro__card-icon"> |
|
<svg width="0" height="0"> |
|
<defs> |
|
<linearGradient |
|
id="icon-grad" |
|
x1="0%" |
|
y1="0%" |
|
x2="100%" |
|
y2="100%" |
|
> |
|
<stop offset="0%" stopColor="#C850C0" /> |
|
<stop offset="100%" stopColor="#4158D0" /> |
|
</linearGradient> |
|
</defs> |
|
</svg> |
|
<card.icon |
|
size={36} |
|
strokeWidth={1.5} |
|
stroke="url(#icon-grad)" |
|
/> |
|
</span> |
|
<p className="utm-intro__card-label">{card.label}</p> |
|
</motion.li> |
|
))} |
|
</ul> |
|
</section> |
|
</div> |
|
|
|
{/* ── 02 What is UTM ── */} |
|
<section className="utm-what" ref={whatRef}> |
|
<div className="inner-wrap"> |
|
<motion.div |
|
initial={{ opacity: 0, y: 40 }} |
|
animate={whatInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.8, ease }} |
|
> |
|
<div className="utm-what__top"> |
|
<motion.span |
|
className="utm-what__eyebrow" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={whatInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, ease }} |
|
> |
|
What is UTM |
|
</motion.span> |
|
<motion.h2 |
|
className="utm-what__title" |
|
initial={{ opacity: 0, y: 20 }} |
|
animate={whatInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.1, ease }} |
|
> |
|
UTM, <em>무엇을 할 수 있나요?</em> |
|
</motion.h2> |
|
<motion.p |
|
className="utm-what__desc" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={whatInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, delay: 0.2, ease }} |
|
> |
|
UTM은 단순한 관제 시스템을 넘어, 도심 저고도 공역 전체를 |
|
디지털로 운영하는 통합 플랫폼입니다. |
|
<br /> |
|
실시간 감시부터 충돌 위험 분석, 기상 연계, 공역 관리까지 — |
|
비행의 모든 순간을 안전하게 연결합니다. |
|
</motion.p> |
|
</div> |
|
|
|
<div className="utm-what__body"> |
|
<motion.ul |
|
className="utm-what__cards" |
|
initial={{ opacity: 0, x: -24 }} |
|
animate={whatInView ? { opacity: 1, x: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.3, ease }} |
|
> |
|
{UTM_WHAT_LEFT.map(({ icon: Icon, label }) => ( |
|
<li key={label} className="utm-what__card"> |
|
<span className="utm-what__card-icon"> |
|
<Icon size={16} /> |
|
</span> |
|
<span className="utm-what__card-label">{label}</span> |
|
</li> |
|
))} |
|
</motion.ul> |
|
|
|
<motion.div |
|
className="utm-what__mockup" |
|
initial={{ opacity: 0, y: 24 }} |
|
animate={whatInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.8, delay: 0.2, ease }} |
|
> |
|
<div |
|
className="utm-what__img-wrap" |
|
style={{ |
|
backgroundImage: `url(${basePath}images/what_utm_img2.png)`, |
|
}} |
|
> |
|
<div className="utm-map-menu"> |
|
<button className="utm-map-menu__btn"> |
|
<Plane size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<ShieldAlert size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<Cloud size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<Layers size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<Radio size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<Database size={16} /> |
|
</button> |
|
<button className="utm-map-menu__btn"> |
|
<Bell size={16} /> |
|
</button> |
|
</div> |
|
|
|
<div |
|
className="drone drone--1" |
|
style={{ top: "35%", left: "48%" }} |
|
> |
|
<div className="drone__badge drone__badge--red"> |
|
<div className="drone__badge-top"> |
|
<img |
|
src={`${basePath}images/red_drone.png`} |
|
alt="drone" |
|
width="16" |
|
height="16" |
|
/> |
|
<span className="drone__badge-conn">PA001</span> |
|
</div> |
|
<span className="drone__badge-id">COLL-ACR-001</span> |
|
<span className="drone__badge-status"> |
|
13m/s / 100m |
|
</span> |
|
</div> |
|
<div className="drone__ping drone__ping--red" /> |
|
<div className="drone__ping drone__ping--red drone__ping--delay" /> |
|
</div> |
|
|
|
<div |
|
className="drone drone--2" |
|
style={{ top: "25%", left: "22%" }} |
|
> |
|
<div className="drone__badge drone__badge--blue"> |
|
<div className="drone__badge-top"> |
|
<img |
|
src={`${basePath}images/blue_drone.png`} |
|
alt="drone" |
|
width="16" |
|
height="16" |
|
/> |
|
<span className="drone__badge-conn">BLE</span> |
|
</div> |
|
<span className="drone__badge-id">DJI-TEST-0005</span> |
|
<span className="drone__badge-status"> |
|
LTE · 9m/s / 100m |
|
</span> |
|
</div> |
|
<div className="drone__ping drone__ping--blue" /> |
|
<div className="drone__ping drone__ping--blue drone__ping--delay" /> |
|
</div> |
|
|
|
<div |
|
className="drone drone--3" |
|
style={{ top: "58%", left: "72%" }} |
|
> |
|
<div className="drone__badge drone__badge--orange"> |
|
<div className="drone__badge-top"> |
|
<img |
|
src={`${basePath}images/orange_drone.png`} |
|
alt="drone" |
|
width="16" |
|
height="16" |
|
/> |
|
<span className="drone__badge-conn">LTE</span> |
|
</div> |
|
<span className="drone__badge-id">FPA502</span> |
|
<span className="drone__badge-status"> |
|
338m / 172k/m / 81° |
|
<br /> |
|
126.745021/37.287339 |
|
</span> |
|
</div> |
|
<div className="drone__ping drone__ping--orange" /> |
|
<div className="drone__ping drone__ping--orange drone__ping--delay" /> |
|
</div> |
|
</div> |
|
</motion.div> |
|
|
|
<motion.ul |
|
className="utm-what__cards utm-what__cards--right" |
|
initial={{ opacity: 0, x: 24 }} |
|
animate={whatInView ? { opacity: 1, x: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.3, ease }} |
|
> |
|
{UTM_WHAT_RIGHT.map(({ icon: Icon, label }) => ( |
|
<li |
|
key={label} |
|
className="utm-what__card utm-what__card--right" |
|
> |
|
<span className="utm-what__card-label">{label}</span> |
|
<span className="utm-what__card-icon"> |
|
<Icon size={16} /> |
|
</span> |
|
</li> |
|
))} |
|
</motion.ul> |
|
</div> |
|
</motion.div> |
|
</div> |
|
</section> |
|
|
|
{/* ── 03 Evolution ── */} |
|
<div className="inner-wrap"> |
|
<section className="utm-evo" ref={evoRef}> |
|
<motion.div |
|
className="utm-evo__left" |
|
initial={{ opacity: 0, x: -32 }} |
|
animate={evoInView ? { opacity: 1, x: 0 } : {}} |
|
transition={{ duration: 0.7, ease }} |
|
> |
|
<motion.span |
|
className="fc-eyebrow" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={evoInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, ease }} |
|
> |
|
Evolution |
|
</motion.span> |
|
<h2 className="utm-evo__title"> |
|
UTM에서 UATM으로, |
|
<br /> |
|
<em>하늘길의 패러다임이 바뀝니다</em> |
|
</h2> |
|
<p className="utm-evo__desc"> |
|
드론 한 대의 비행에서 시작된 기술이, 이제 도심 전체의 하늘을 |
|
통합 관제하는 UATM으로 진화했습니다. PAL Networks는 이 변화의 |
|
최전선에서 기체와 관제를 함께 운영합니다. |
|
</p> |
|
</motion.div> |
|
|
|
<div className="utm-evo__right"> |
|
<div className="utm-evo__track" ref={evoTrackRef}> |
|
<div className="utm-evo__line-bg" /> |
|
<motion.div |
|
className="utm-evo__line-fill" |
|
style={{ height: lineHeight }} |
|
/> |
|
{EVO_ITEMS.map((item, i) => ( |
|
<EvoItem key={item.name} item={item} index={i} /> |
|
))} |
|
</div> |
|
</div> |
|
</section> |
|
</div> |
|
|
|
{/* ── 04 Capabilities ── */} |
|
<section className="utm-cap" ref={capRef}> |
|
<div className="inner-wrap"> |
|
<div className="utm-cap__top"> |
|
<motion.span |
|
className="fc-eyebrow" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={capInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, ease }} |
|
> |
|
Capabilities |
|
</motion.span> |
|
<motion.h2 |
|
className="utm-cap__title" |
|
initial={{ opacity: 0, y: 20 }} |
|
animate={capInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.1, ease }} |
|
> |
|
PAL Networks의 <br /> |
|
<em>세 가지 핵심 역량</em> |
|
</motion.h2> |
|
<motion.p |
|
className="utm-cap__desc" |
|
initial={{ opacity: 0, y: 16 }} |
|
animate={capInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.6, delay: 0.2, ease }} |
|
> |
|
기체 운용부터 관제, 데이터까지 — 하나의 사업자가 모두 보유한 |
|
통합 역량입니다. |
|
</motion.p> |
|
</div> |
|
|
|
<ul className="utm-cap__cards"> |
|
{SECTORS.map((s, i) => ( |
|
<motion.li |
|
key={s.id} |
|
className="utm-cap__card" |
|
style={{ "--cap-color": s.color }} |
|
initial={{ opacity: 0, y: 48 }} |
|
animate={capInView ? { opacity: 1, y: 0 } : {}} |
|
transition={{ duration: 0.7, delay: 0.2 + i * 0.12, ease }} |
|
> |
|
<span className="utm-cap__card-num">{s.num}</span> |
|
<div className="utm-cap__card-body"> |
|
<strong className="utm-cap__card-name">{s.name}</strong> |
|
<ul className="utm-cap__card-tags"> |
|
{s.tags.map((tag) => ( |
|
<li key={tag}>{tag}</li> |
|
))} |
|
</ul> |
|
</div> |
|
</motion.li> |
|
))} |
|
</ul> |
|
</div> |
|
</section> |
|
</div> |
|
</article> |
|
); |
|
} |
|
|
|
export default IntroPage;
|
|
|