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.
 
 
 

621 lines
23 KiB

import { useRef, useState } from "react";
import { motion, useInView, AnimatePresence } from "framer-motion";
import {
Radio,
Puzzle,
Network,
Expand,
Shield,
ArrowUpRight,
Package,
Wind,
AlertTriangle,
Ship,
Plane,
} from "lucide-react";
import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn";
const SOLUTION_NAV = [
{ label: "비행상황관리 시스템", to: "/solution/flight-control" },
{ label: "IBE", to: "/solution/ibe" },
// { label: "스마트 관광 예약 플랫폼", to: "/solution/smart-tour" },
// { label: "KT G-cloud 인천총판", to: "/solution/kt-gcloud" },
];
const FEATURES = [
{ icon: Radio, label: "실시간 모니터링" },
{ icon: Puzzle, label: "손쉬운 연계모듈" },
{ icon: Network, label: "네트워크 기반 방식" },
{ icon: Expand, label: "확장성 고려" },
{ icon: Shield, label: "안정적 서비스" },
];
const STATS = [
{ value: "90%+", label: "운항 데이터 정확도" },
{ value: "10분", label: "이내 실시간 처리" },
{ value: "24/7", label: "무중단 모니터링" },
{ value: "N+1", label: "다중 비행체 동시 관제" },
];
const DOMAINS = [
{
img: "domain_img1.jpg",
label: "드론 물류·배송 관제",
desc: "도심 및 광역 드론 배송 경로 최적화 및 실시간 위치 관제",
},
{
img: "domain_img2.jpg",
label: "UAM 도심항공 모빌리티",
desc: "미래형 도심 항공 교통 인프라 연계 관제 시스템",
},
{
img: "domain_img3.jpg",
label: "무인기 환경측정",
desc: "대기질·기상 데이터 수집 드론 운용 및 모니터링",
},
{
img: "domain_img4.jpg",
label: "안티드론 보안관제",
desc: "비인가 드론 탐지·추적 및 무력화 대응 지원",
},
{
img: "domain_img5.jpg",
label: "선박·항공기 통합관제",
desc: "해상·공중 복합 식별정보 수집 및 통합 상황관리",
},
];
const FUNCTIONS = [
{ num: "01", img: "./images/s1-01.jpg", label: "비행가능 지역 및 공역표출" },
{ num: "02", img: "./images/s1-02.jpg", label: "비행체 위치 표출" },
{
num: "03",
img: "./images/s1-03.jpg",
label: "항공기 비행경로 및 히스토리 표출",
},
{ num: "04", img: "./images/s1-04.jpg", label: "항공기 비행정보 표출" },
{ num: "05", img: "./images/s1-05.jpg", label: "비행계획서 조회" },
{ num: "06", img: "./images/s1-06.jpg", label: "비정상 상황의 경보 표출" },
];
const FLOW = [
{
step: "01",
label: "비행체 식별",
desc: "항공기·드론·선박 실시간 신호 수집",
},
{
step: "02",
label: "데이터 수집",
desc: "위치·속도·고도·식별코드 통합 처리",
},
{ step: "03", label: "관제 서버", desc: "통합 관제 서버 분석 및 상황 판단" },
{ step: "04", label: "모니터링", desc: "관제사 화면 실시간 현황 표출" },
{ step: "05", label: "경보·대응", desc: "이상 상황 감지 즉시 경보 및 대응" },
];
const ease = [0.22, 1, 0.36, 1];
function RevealText({ children, delay = 0, className }) {
return (
<div style={{ overflow: "hidden" }}>
<motion.div
className={className}
initial={{ y: "105%", opacity: 0 }}
animate={{ y: "0%", opacity: 1 }}
transition={{ duration: 0.75, ease: [0.22, 1, 0.36, 1], delay }}
>
{children}
</motion.div>
</div>
);
}
function StaggerWords({ text, delay = 0, className }) {
const words = text.split(" ");
return (
<span
className={className}
style={{ display: "flex", flexWrap: "wrap", gap: "0 .3em" }}
>
{words.map((word, i) => (
<span key={i} style={{ overflow: "hidden", display: "inline-block" }}>
<motion.span
style={{ display: "inline-block" }}
initial={{ y: "110%", opacity: 0 }}
animate={{ y: "0%", opacity: 1 }}
transition={{
duration: 0.65,
ease: [0.22, 1, 0.36, 1],
delay: delay + i * 0.055,
}}
>
{word}
</motion.span>
</span>
))}
</span>
);
}
function FlightControlPage() {
const basePath = import.meta.env.BASE_URL;
const ref = useFadeIn();
const [activeIdx, setActiveIdx] = useState(0);
// const overviewRef = useRef(null);
const statsRef = useRef(null);
const domainsRef = useRef(null);
const funcRef = useRef(null);
const flowRef = useRef(null);
const introRef = useRef(null);
const introInView = useInView(introRef, { once: true, margin: "-60px" });
const highlightRef = useRef(null);
const highlightInView = useInView(highlightRef, {
once: true,
margin: "-60px",
});
// const overviewInView = useInView(overviewRef, {
// once: true,
// margin: "-60px",
// });
const statsInView = useInView(statsRef, { once: true, margin: "-60px" });
const domainsInView = useInView(domainsRef, { once: true, margin: "-60px" });
const funcInView = useInView(funcRef, { once: true, margin: "-80px" });
const flowInView = useInView(flowRef, { once: true, margin: "-60px" });
return (
<article ref={ref}>
<SubHero
label="SOLUTION"
title={<em>Flight Management</em>}
navItems={SOLUTION_NAV}
/>
<div className="sub-content">
<div className="inner-wrap">
{/* 1. 개요 */}
{/* <section className="fc-overview" ref={overviewRef}>
<motion.img
src="./images/test111.png"
alt=""
className="fc-overview__bg"
initial={{ scale: 1.08 }}
animate={overviewInView ? { scale: 1 } : {}}
transition={{ duration: 2.2, ease: [0.22, 1, 0.36, 1] }}
/>
<div className="fc-overview__overlay" />
<div className="fc-overview__content">
{overviewInView && (
<RevealText
delay={0.1}
className="fc-eyebrow fc-eyebrow--light"
>
비행상황 관리 시스템
</RevealText>
)}
{overviewInView && (
<div className="fc-overview__title">
<StaggerWords
text="항공기, 무인기, 선박 등 실시간 식별정보를 통하여 모니터링 및 상황관리를 할 수 있는 시스템"
delay={0.18}
className="fc-overview__title-inner"
/>
</div>
)}
<motion.div
className="fc-overview__bottom"
initial={{ opacity: 0, filter: "blur(8px)", y: 12 }}
animate={
overviewInView
? { opacity: 1, filter: "blur(0px)", y: 0 }
: {}
}
transition={{ duration: 0.85, ease, delay: 0.85 }}
>
<p className="fc-overview__sub">
각종 드론 상황관제, 환경측정, 미래산업인 드론/PAV/UAM을 활용한
사업들을 지원하기 위한 환경/물류/안티 드론 등의 상황관제
시스템을 연구하고 솔루션을 제공합니다.
</p>
<div className="fc-badges">
{FEATURES.map(({ icon: Icon, label }, i) => (
<motion.span
key={label}
className="fc-badge fc-badge--light"
initial={{ opacity: 0, y: 8 }}
animate={overviewInView ? { opacity: 1, y: 0 } : {}}
transition={{
duration: 0.4,
ease,
delay: 0.95 + i * 0.07,
}}
>
<Icon size={12} strokeWidth={2} />
{label}
</motion.span>
))}
</div>
</motion.div>
</div>
</section> */}
{/* 개요 인트로 */}
<section className="fc-intro" ref={introRef}>
<div className="fc-intro__left">
<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="fc-intro__title"
initial={{ opacity: 0, y: 24 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, ease, delay: 0.1 }}
>
모든 이동체 정보를
<br />
하나의 플랫폼에서
</motion.h2>
<motion.p
className="fc-intro__desc"
initial={{ opacity: 0, y: 16 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease, delay: 0.2 }}
>
항공기, 무인기, 선박, 지상 이동체의 실시간 정보를 통합
모니터링하고 <br />
상황을 인식하여 신속하고 안전한 운영 의사결정을 지원합니다.
</motion.p>
<motion.div
className="fc-intro__icons"
initial={{ opacity: 0, y: 16 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease, delay: 0.3 }}
>
{[
{ img: "fc_aircraft.png", label: "항공기" },
{ img: "fc_drone.png", label: "무인기" },
{ img: "fc_vessel.png", label: "선박" },
{ img: "fc_car.png", label: "차량 등" },
].map((item, i) => (
<div key={i} className="fc-intro__icon-item">
<img
src={`${basePath}images/${item.img}`}
alt={item.label}
/>
<span>{item.label}</span>
</div>
))}
</motion.div>
</div>
<motion.div
className="fc-intro__right"
initial={{ opacity: 0, x: 40 }}
animate={introInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.9, ease, delay: 0.15 }}
>
<img
src={`${basePath}images/fc_computer.png`}
alt="비행상황관리 시스템"
className="fc-intro__monitor"
/>
</motion.div>
</section>
{/* 기능 하이라이트 */}
<section className="fc-highlight" ref={highlightRef}>
{[
{
tag: "Real-Time Monitoring",
title: "실시간 이동체 정보 기반\n통합 모니터링",
desc: "전체에 분산된 이동체의 위치와 상태를 실시간으로 추적하여 안전하고 효율적인 운영 환경을 제공합니다.",
img: "fc_tablet.png",
icons: [
{
img: "fc_tab_airplane.png",
x: "0%",
y: "8%",
delay: 0,
move: { x: [-40, -120, 0], y: [0, 90, 0] }, // 왼쪽 아래 (배 방향)
},
{
img: "fc_tab_vessel.png",
x: "-30%",
y: "42%",
delay: 0,
move: { x: [0, 110, 0], y: [0, 60, 0] }, // 오른쪽 아래 (자동차 방향)
},
{
img: "fc_tab_car.png",
x: "0%",
y: "64%",
delay: 0,
move: { x: [0, 120, 0], y: [0, -65, 0] }, // 오른쪽 위 (드론 방향)
},
{
img: "fc_tab_drone.png",
x: "30%",
y: "40%",
delay: 0,
move: { x: [0, -100, 0], y: [0, -50, 0] }, // 왼쪽 위 (비행기 방향)
},
],
},
{
tag: "Situational Awareness",
title: "상황 인식 및\n의사결정 지원",
desc: "다양한 데이터와 고도화된 분석을 통해 상황을 예측하고 최적의 의사결정을 가능하게 합니다.",
img: "fc_tablet2.png",
icons: null,
},
].map((item, i) => (
<motion.div
key={i}
className="fc-highlight__item"
initial={{ opacity: 0, y: 40 }}
animate={highlightInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, ease, delay: i * 0.15 }}
>
<div className="fc-highlight__text">
<span className="fc-eyebrow">{item.tag}</span>
<h3 className="fc-highlight__title">
{item.title.split("\n").map((line, j) => (
<span key={j}>
{line}
<br />
</span>
))}
</h3>
<p className="fc-highlight__desc">{item.desc}</p>
</div>
<div className="fc-highlight__img-wrap">
<div className="fc-highlight__img-scene">
<img
src={`${basePath}images/${item.img}`}
alt={item.tag}
className="fc-highlight__tablet"
/>
{item.icons &&
item.icons.map((icon, j) => (
<motion.img
key={j}
src={`${basePath}images/${icon.img}`}
alt=""
className="fc-highlight__float-icon"
style={{ left: icon.x, top: icon.y }}
initial={{ x: 0, y: 0 }}
animate={
highlightInView
? { x: icon.move.x[1], y: icon.move.y[1] }
: { x: 0, y: 0 }
}
transition={{
duration: 2.5,
repeat: 0,
delay: icon.delay,
ease: "easeOut",
}}
/>
))}
{i === 1 && (
<>
<motion.img
src={`${basePath}images/fc_left_tab.png`}
alt=""
className="fc-situation__left"
initial={{ opacity: 0, x: -30 }}
animate={highlightInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.8, ease, delay: 0.3 }}
/>
<motion.img
src={`${basePath}images/fc_right_tab.png`}
alt=""
className="fc-situation__right"
initial={{ opacity: 0, x: 30 }}
animate={highlightInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.8, ease, delay: 0.5 }}
/>
<motion.img
src={`${basePath}images/fc_bottom_tab.png`}
alt=""
className="fc-situation__bottom"
initial={{ opacity: 0, y: 30 }}
animate={highlightInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, ease, delay: 0.7 }}
/>
</>
)}
</div>
</div>
</motion.div>
))}
</section>
{/* 2. 수치 지표 */}
{/* <section className="fc-stats" ref={statsRef}>
{STATS.map(({ value, label }, i) => (
<motion.div
key={label}
className="fc-stat-item"
initial={{ opacity: 0, y: 24 }}
animate={statsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.55, ease, delay: 0.08 * i }}
>
<span className="fc-stat-item__value">{value}</span>
<span className="fc-stat-item__label">{label}</span>
</motion.div>
))}
</section> */}
{/* 3. 적용 분야 */}
<section className="fc-domains" ref={domainsRef}>
<motion.span
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={domainsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
적용 분야
</motion.span>
<div className="fc-domains__grid">
{DOMAINS.map(({ icon: Icon, img, label, desc }, i) => (
<motion.div
key={label}
className="fc-domain-card"
initial={{ opacity: 0, y: 24 }}
animate={domainsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.55, ease, delay: 0.07 * i }}
>
<img
className="fc-domain-card__img"
src={`${basePath}images/${img}`}
alt={label}
/>
<div className="fc-domain-card__overlay">
<span className="fc-domain-card__label">{label}</span>
<p className="fc-domain-card__desc">{desc}</p>
</div>
</motion.div>
))}
</div>
</section>
{/* 4. 주요기능 */}
<section className="fc-functions" ref={funcRef}>
<motion.span
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={funcInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
주요기능
</motion.span>
<div className="fc-functions__body">
<ul className="fc-func-list">
{FUNCTIONS.map(({ num, label }, i) => (
<motion.li
key={num}
className={`fc-func-item${activeIdx === i ? " is-active" : ""}`}
onMouseEnter={() => setActiveIdx(i)}
initial={{ opacity: 0, y: 16 }}
animate={funcInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, ease, delay: 0.05 * i }}
>
<span className="fc-func-item__num">{num}</span>
<span className="fc-func-item__label">{label}</span>
<motion.span
className="fc-func-item__arrow"
initial={{ opacity: 0, x: -6 }}
animate={
activeIdx === i
? { opacity: 1, x: 0 }
: { opacity: 0, x: -6 }
}
transition={{ duration: 0.2 }}
>
<ArrowUpRight size={16} strokeWidth={1.5} />
</motion.span>
<motion.div
className="fc-func-item__line"
animate={{ scaleX: activeIdx === i ? 1 : 0 }}
transition={{ duration: 0.35, ease }}
/>
</motion.li>
))}
</ul>
<div className="fc-func-display">
<AnimatePresence mode="wait">
<motion.div
key={activeIdx}
className="fc-func-display__inner"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.35, ease: "easeInOut" }}
>
<img
src={FUNCTIONS[activeIdx].img}
alt={FUNCTIONS[activeIdx].label}
className="fc-func-display__img"
/>
<div className="fc-func-display__caption">
<motion.span
key={`num-${activeIdx}`}
className="fc-func-display__num"
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
>
{FUNCTIONS[activeIdx].num}
</motion.span>
<motion.span
key={`label-${activeIdx}`}
className="fc-func-display__label"
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25, delay: 0.05 }}
>
{FUNCTIONS[activeIdx].label}
</motion.span>
</div>
</motion.div>
</AnimatePresence>
</div>
</div>
</section>
{/* 5. 시스템 구성 흐름 */}
<section className="fc-flow" ref={flowRef}>
<motion.span
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={flowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
시스템 구성
</motion.span>
<div className="fc-flow__row">
{FLOW.map(({ step, label, desc }, i) => (
<motion.div
key={step}
className="fc-flow__item"
initial={{ opacity: 0, y: 20 }}
animate={flowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, ease, delay: 0.08 * i }}
>
<span className="fc-flow__step">{step}</span>
<span className="fc-flow__label">{label}</span>
<p className="fc-flow__desc">{desc}</p>
{i < FLOW.length - 1 && (
<motion.div
className="fc-flow__arrow"
initial={{ opacity: 0, scaleX: 0 }}
animate={flowInView ? { opacity: 1, scaleX: 1 } : {}}
transition={{
duration: 0.4,
ease,
delay: 0.08 * i + 0.3,
}}
/>
)}
</motion.div>
))}
</div>
</section>
</div>
</div>
</article>
);
}
export default FlightControlPage;