Browse Source

feat : 비행상황관리 실사 이미지 수정 및 수치 주석 처리

remotes/origin/main
이시연 3 weeks ago
parent
commit
9bcd357f1b
  1. BIN
      public/images/domain_img1.png
  2. BIN
      public/images/domain_img2.png
  3. BIN
      public/images/domain_img3.png
  4. BIN
      public/images/domain_img4.png
  5. BIN
      public/images/domain_img5.png
  6. 21
      src/css/common.css
  7. 268
      src/pages/solution/FlightControlPage.jsx

BIN
public/images/domain_img1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
public/images/domain_img2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
public/images/domain_img3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
public/images/domain_img4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
public/images/domain_img5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

21
src/css/common.css

@ -735,14 +735,21 @@ body{overflow-x:hidden;}
5. 적용 분야 5. 적용 분야
*/ */
.fc-domains { margin-bottom: 8rem; } .fc-domains { margin-bottom: 8rem; }
.fc-domains__grid {display:grid;grid-template-columns:repeat(5,1fr);gap:1.25rem;} .fc-domains__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1.25rem; }
.fc-domain-card {display:flex;flex-direction:column;gap:.75rem;padding:1.75rem 1.5rem;border:1px solid var(--color-primary-soft-border);border-radius:1rem;background:#fff;transition:border-color .2s,box-shadow .2s;cursor:default;}
.fc-domain-card:hover {border-color:var(--color-primary-border);box-shadow:0 8px 28px var(--color-primary-shadow);}
.fc-domain-card__icon {width:44px;height:44px;border-radius:.75rem;background:var(--color-primary-soft);display:flex;align-items:center;justify-content:center;color:var(--color-primary);transition:background .2s,color .2s;}
.fc-domain-card:hover .fc-domain-card__icon {background:var(--color-primary);color:#fff;}
.fc-domain-card__label {font-size:.88rem;font-weight:700;color:var(--color-primary);line-height:1.3;}
.fc-domain-card__desc {font-size:.78rem;line-height:1.65;color:#888;margin:0;}
.fc-domain-card { display: flex; flex-direction: column; border: 1px solid var(--color-primary-soft-border); border-radius: 1rem; background: #fff; overflow: hidden; transition: border-color .2s, box-shadow .2s; cursor: default; }
.fc-domain-card:hover { border-color: var(--color-primary-border); box-shadow: 0 8px 28px var(--color-primary-shadow); }
.fc-domain-card__img { width: 100%; aspect-ratio: 16/10; overflow: hidden; }
.fc-domain-card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
.fc-domain-card:hover .fc-domain-card__img img { transform: scale(1.05); }
.fc-domain-card__body { display: flex; flex-direction: column; gap: .5rem; padding: 1.25rem 1.25rem 1.5rem; }
.fc-domain-card__label { font-size: .88rem; font-weight: 700; color: var(--color-primary); line-height: 1.3; }
.fc-domain-card__desc { font-size: .78rem; line-height: 1.65; color: #888; margin: 0; }
@media (max-width: 1024px) { .fc-domains__grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px) { .fc-domains__grid { grid-template-columns: repeat(2, 1fr); } }
/* /*
6. 주요기능 6. 주요기능
*/ */

268
src/pages/solution/FlightControlPage.jsx

@ -1,6 +1,18 @@
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { motion, useInView, AnimatePresence } from "framer-motion"; import { motion, useInView, AnimatePresence } from "framer-motion";
import { Radio, Puzzle, Network, Expand, Shield, ArrowUpRight, Package, Wind, AlertTriangle, Ship, Plane } from "lucide-react"; import {
Radio,
Puzzle,
Network,
Expand,
Shield,
ArrowUpRight,
Package,
Wind,
AlertTriangle,
Ship,
Plane,
} from "lucide-react";
import SubHero from "../../components/SubHero"; import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn"; import useFadeIn from "../../hooks/useFadeIn";
@ -28,27 +40,27 @@ const STATS = [
const DOMAINS = [ const DOMAINS = [
{ {
icon: Package, img: "domain_img1.png",
label: "드론 물류·배송 관제", label: "드론 물류·배송 관제",
desc: "도심 및 광역 드론 배송 경로 최적화 및 실시간 위치 관제", desc: "도심 및 광역 드론 배송 경로 최적화 및 실시간 위치 관제",
}, },
{ {
icon: Plane, img: "domain_img2.png",
label: "UAM 도심항공 모빌리티", label: "UAM 도심항공 모빌리티",
desc: "미래형 도심 항공 교통 인프라 연계 관제 시스템", desc: "미래형 도심 항공 교통 인프라 연계 관제 시스템",
}, },
{ {
icon: Wind, img: "domain_img3.png",
label: "무인기 환경측정", label: "무인기 환경측정",
desc: "대기질·기상 데이터 수집 드론 운용 및 모니터링", desc: "대기질·기상 데이터 수집 드론 운용 및 모니터링",
}, },
{ {
icon: AlertTriangle, img: "domain_img4.png",
label: "안티드론 보안관제", label: "안티드론 보안관제",
desc: "비인가 드론 탐지·추적 및 무력화 대응 지원", desc: "비인가 드론 탐지·추적 및 무력화 대응 지원",
}, },
{ {
icon: Ship, img: "domain_img5.png",
label: "선박·항공기 통합관제", label: "선박·항공기 통합관제",
desc: "해상·공중 복합 식별정보 수집 및 통합 상황관리", desc: "해상·공중 복합 식별정보 수집 및 통합 상황관리",
}, },
@ -88,7 +100,12 @@ const ease = [0.22, 1, 0.36, 1];
function RevealText({ children, delay = 0, className }) { function RevealText({ children, delay = 0, className }) {
return ( return (
<div style={{ overflow: "hidden" }}> <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 }}> <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} {children}
</motion.div> </motion.div>
</div> </div>
@ -98,7 +115,10 @@ function RevealText({ children, delay = 0, className }) {
function StaggerWords({ text, delay = 0, className }) { function StaggerWords({ text, delay = 0, className }) {
const words = text.split(" "); const words = text.split(" ");
return ( return (
<span className={className} style={{ display: "flex", flexWrap: "wrap", gap: "0 .3em" }}> <span
className={className}
style={{ display: "flex", flexWrap: "wrap", gap: "0 .3em" }}
>
{words.map((word, i) => ( {words.map((word, i) => (
<span key={i} style={{ overflow: "hidden", display: "inline-block" }}> <span key={i} style={{ overflow: "hidden", display: "inline-block" }}>
<motion.span <motion.span
@ -125,7 +145,7 @@ function FlightControlPage() {
const ref = useFadeIn(); const ref = useFadeIn();
const [activeIdx, setActiveIdx] = useState(0); const [activeIdx, setActiveIdx] = useState(0);
const overviewRef = useRef(null); // const overviewRef = useRef(null);
const statsRef = useRef(null); const statsRef = useRef(null);
const domainsRef = useRef(null); const domainsRef = useRef(null);
const funcRef = useRef(null); const funcRef = useRef(null);
@ -137,10 +157,10 @@ function FlightControlPage() {
once: true, once: true,
margin: "-60px", margin: "-60px",
}); });
const overviewInView = useInView(overviewRef, { // const overviewInView = useInView(overviewRef, {
once: true, // once: true,
margin: "-60px", // margin: "-60px",
}); // });
const statsInView = useInView(statsRef, { once: true, margin: "-60px" }); const statsInView = useInView(statsRef, { once: true, margin: "-60px" });
const domainsInView = useInView(domainsRef, { once: true, margin: "-60px" }); const domainsInView = useInView(domainsRef, { once: true, margin: "-60px" });
const funcInView = useInView(funcRef, { once: true, margin: "-80px" }); const funcInView = useInView(funcRef, { once: true, margin: "-80px" });
@ -148,7 +168,11 @@ function FlightControlPage() {
return ( return (
<article ref={ref}> <article ref={ref}>
<SubHero label="SOLUTION" title={<em>Flight Management</em>} navItems={SOLUTION_NAV} /> <SubHero
label="SOLUTION"
title={<em>Flight Management</em>}
navItems={SOLUTION_NAV}
/>
<div className="sub-content"> <div className="sub-content">
<div className="inner-wrap"> <div className="inner-wrap">
@ -221,19 +245,40 @@ function FlightControlPage() {
{/* 개요 인트로 */} {/* 개요 인트로 */}
<section className="fc-intro" ref={introRef}> <section className="fc-intro" ref={introRef}>
<div className="fc-intro__left"> <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 }}> <motion.span
className="fc-eyebrow"
initial={{ opacity: 0, y: 16 }}
animate={introInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
Overview Overview
</motion.span> </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 }}> <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 /> <br />
하나의 플랫폼에서 하나의 플랫폼에서
</motion.h2> </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 }}> <motion.p
항공기, 무인기, 선박, 지상 이동체의 실시간 정보를 통합 모니터링하고 <br /> 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.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 }}> <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_aircraft.png", label: "항공기" },
{ img: "fc_drone.png", label: "무인기" }, { img: "fc_drone.png", label: "무인기" },
@ -241,15 +286,27 @@ function FlightControlPage() {
{ img: "fc_car.png", label: "차량 등" }, { img: "fc_car.png", label: "차량 등" },
].map((item, i) => ( ].map((item, i) => (
<div key={i} className="fc-intro__icon-item"> <div key={i} className="fc-intro__icon-item">
<img src={`${basePath}images/${item.img}`} alt={item.label} /> <img
src={`${basePath}images/${item.img}`}
alt={item.label}
/>
<span>{item.label}</span> <span>{item.label}</span>
</div> </div>
))} ))}
</motion.div> </motion.div>
</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 }}> <motion.div
<img src={`${basePath}images/fc_computer.png`} alt="비행상황관리 시스템" className="fc-intro__monitor" /> 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> </motion.div>
</section> </section>
@ -302,7 +359,13 @@ function FlightControlPage() {
icons: null, icons: null,
}, },
].map((item, i) => ( ].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 }}> <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"> <div className="fc-highlight__text">
<span className="fc-eyebrow">{item.tag}</span> <span className="fc-eyebrow">{item.tag}</span>
<h3 className="fc-highlight__title"> <h3 className="fc-highlight__title">
@ -317,7 +380,11 @@ function FlightControlPage() {
</div> </div>
<div className="fc-highlight__img-wrap"> <div className="fc-highlight__img-wrap">
<div className="fc-highlight__img-scene"> <div className="fc-highlight__img-scene">
<img src={`${basePath}images/${item.img}`} alt={item.tag} className="fc-highlight__tablet" /> <img
src={`${basePath}images/${item.img}`}
alt={item.tag}
className="fc-highlight__tablet"
/>
{item.icons && {item.icons &&
item.icons.map((icon, j) => ( item.icons.map((icon, j) => (
<motion.img <motion.img
@ -327,7 +394,11 @@ function FlightControlPage() {
className="fc-highlight__float-icon" className="fc-highlight__float-icon"
style={{ left: icon.x, top: icon.y }} style={{ left: icon.x, top: icon.y }}
initial={{ x: 0, y: 0 }} initial={{ x: 0, y: 0 }}
animate={highlightInView ? { x: icon.move.x[1], y: icon.move.y[1] } : { x: 0, y: 0 }} animate={
highlightInView
? { x: icon.move.x[1], y: icon.move.y[1] }
: { x: 0, y: 0 }
}
transition={{ transition={{
duration: 2.5, duration: 2.5,
repeat: 0, repeat: 0,
@ -339,9 +410,30 @@ function FlightControlPage() {
{i === 1 && ( {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
<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 }} /> src={`${basePath}images/fc_left_tab.png`}
<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 }} /> 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>
@ -350,60 +442,127 @@ function FlightControlPage() {
))} ))}
</section> </section>
{/* 2. 수치 지표 */} {/* 2. 수치 지표 */}
<section className="fc-stats" ref={statsRef}> {/* <section className="fc-stats" ref={statsRef}>
{STATS.map(({ value, label }, i) => ( {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 }}> <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__value">{value}</span>
<span className="fc-stat-item__label">{label}</span> <span className="fc-stat-item__label">{label}</span>
</motion.div> </motion.div>
))} ))}
</section> </section> */}
{/* 3. 적용 분야 */} {/* 3. 적용 분야 */}
<section className="fc-domains" ref={domainsRef}> <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
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={domainsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
적용 분야 적용 분야
</motion.span> </motion.span>
<div className="fc-domains__grid"> <div className="fc-domains__grid">
{DOMAINS.map(({ icon: Icon, label, desc }, i) => ( {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 }} whileHover={{ y: -4, transition: { duration: 0.2 } }}> <motion.div
<div className="fc-domain-card__icon"> key={label}
<Icon size={22} strokeWidth={1.5} /> className="fc-domain-card"
initial={{ opacity: 0, y: 24 }}
animate={domainsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.55, ease, delay: 0.07 * i }}
whileHover={{ y: -4, transition: { duration: 0.2 } }}
>
<div className="fc-domain-card__img">
<img src={`${basePath}images/${img}`} alt={label} />
</div>
<div className="fc-domain-card__body">
<span className="fc-domain-card__label">{label}</span>
<p className="fc-domain-card__desc">{desc}</p>
</div> </div>
<span className="fc-domain-card__label">{label}</span>
<p className="fc-domain-card__desc">{desc}</p>
</motion.div> </motion.div>
))} ))}
</div> </div>
</section> </section>
{/* 4. 주요기능 */} {/* 4. 주요기능 */}
<section className="fc-functions" ref={funcRef}> <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
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={funcInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
주요기능 주요기능
</motion.span> </motion.span>
<div className="fc-functions__body"> <div className="fc-functions__body">
<ul className="fc-func-list"> <ul className="fc-func-list">
{FUNCTIONS.map(({ num, label }, i) => ( {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 }}> <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__num">{num}</span>
<span className="fc-func-item__label">{label}</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 }}> <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} /> <ArrowUpRight size={16} strokeWidth={1.5} />
</motion.span> </motion.span>
<motion.div className="fc-func-item__line" animate={{ scaleX: activeIdx === i ? 1 : 0 }} transition={{ duration: 0.35, ease }} /> <motion.div
className="fc-func-item__line"
animate={{ scaleX: activeIdx === i ? 1 : 0 }}
transition={{ duration: 0.35, ease }}
/>
</motion.li> </motion.li>
))} ))}
</ul> </ul>
<div className="fc-func-display"> <div className="fc-func-display">
<AnimatePresence mode="wait"> <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" }}> <motion.div
<img src={FUNCTIONS[activeIdx].img} alt={FUNCTIONS[activeIdx].label} className="fc-func-display__img" /> 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"> <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 }}> <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} {FUNCTIONS[activeIdx].num}
</motion.span> </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 }}> <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} {FUNCTIONS[activeIdx].label}
</motion.span> </motion.span>
</div> </div>
@ -415,12 +574,23 @@ function FlightControlPage() {
{/* 5. 시스템 구성 흐름 */} {/* 5. 시스템 구성 흐름 */}
<section className="fc-flow" ref={flowRef}> <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
className="fc-section-title"
initial={{ opacity: 0, y: 20 }}
animate={flowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
시스템 구성 시스템 구성
</motion.span> </motion.span>
<div className="fc-flow__row"> <div className="fc-flow__row">
{FLOW.map(({ step, label, desc }, i) => ( {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 }}> <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__step">{step}</span>
<span className="fc-flow__label">{label}</span> <span className="fc-flow__label">{label}</span>
<p className="fc-flow__desc">{desc}</p> <p className="fc-flow__desc">{desc}</p>

Loading…
Cancel
Save