|
|
|
@ -2,24 +2,7 @@ import { useRef, useState, useEffect } from "react"; |
|
|
|
import { motion, AnimatePresence, useInView } from "framer-motion"; |
|
|
|
import { motion, AnimatePresence, useInView } from "framer-motion"; |
|
|
|
import SubHero from "../../components/SubHero"; |
|
|
|
import SubHero from "../../components/SubHero"; |
|
|
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
|
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
|
|
import { |
|
|
|
import { Plane, Globe, UtensilsCrossed, Thermometer, MapPin, Link, QrCode, Users, Siren, Wrench, PlusCircle, Settings, ShieldCheck, Monitor, Building2, ClipboardList } from "lucide-react"; |
|
|
|
Plane, |
|
|
|
|
|
|
|
Globe, |
|
|
|
|
|
|
|
UtensilsCrossed, |
|
|
|
|
|
|
|
Thermometer, |
|
|
|
|
|
|
|
MapPin, |
|
|
|
|
|
|
|
Link, |
|
|
|
|
|
|
|
QrCode, |
|
|
|
|
|
|
|
Users, |
|
|
|
|
|
|
|
Siren, |
|
|
|
|
|
|
|
Wrench, |
|
|
|
|
|
|
|
PlusCircle, |
|
|
|
|
|
|
|
Settings, |
|
|
|
|
|
|
|
ShieldCheck, |
|
|
|
|
|
|
|
Monitor, |
|
|
|
|
|
|
|
Building2, |
|
|
|
|
|
|
|
ClipboardList, |
|
|
|
|
|
|
|
} from "lucide-react"; |
|
|
|
|
|
|
|
const ease = [0.25, 0.1, 0.25, 1]; |
|
|
|
const ease = [0.25, 0.1, 0.25, 1]; |
|
|
|
|
|
|
|
|
|
|
|
const PROJECTS = [ |
|
|
|
const PROJECTS = [ |
|
|
|
@ -48,7 +31,7 @@ const PROJECTS = [ |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: "02", |
|
|
|
id: "02", |
|
|
|
title: "안면관광 방역 시스템 구축", |
|
|
|
title: "안전관광 방역 시스템 구축", |
|
|
|
tags: ["공공/방역", "방역/보안"], |
|
|
|
tags: ["공공/방역", "방역/보안"], |
|
|
|
image: "/images/si_img2.png", |
|
|
|
image: "/images/si_img2.png", |
|
|
|
desc: [ |
|
|
|
desc: [ |
|
|
|
@ -140,7 +123,7 @@ const PROJECTS = [ |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
id: "06", |
|
|
|
id: "06", |
|
|
|
title: "하이에어 항공문항 시스템 구축", |
|
|
|
title: "하이에어 항공운항 시스템 구축", |
|
|
|
tags: ["항공", "인증/스케줄"], |
|
|
|
tags: ["항공", "인증/스케줄"], |
|
|
|
image: "/images/si_img6.png", |
|
|
|
image: "/images/si_img6.png", |
|
|
|
desc: [ |
|
|
|
desc: [ |
|
|
|
@ -222,15 +205,13 @@ function SiPage() { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleDragStart = (e) => { |
|
|
|
const handleDragStart = (e) => { |
|
|
|
dragStartX.current = |
|
|
|
dragStartX.current = e.type === "touchstart" ? e.touches[0].clientX : e.clientX; |
|
|
|
e.type === "touchstart" ? e.touches[0].clientX : e.clientX; |
|
|
|
|
|
|
|
dragStartCurrent.current = current; |
|
|
|
dragStartCurrent.current = current; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleDragEnd = (e) => { |
|
|
|
const handleDragEnd = (e) => { |
|
|
|
if (dragStartX.current === null) return; |
|
|
|
if (dragStartX.current === null) return; |
|
|
|
const endX = |
|
|
|
const endX = e.type === "touchend" ? e.changedTouches[0].clientX : e.clientX; |
|
|
|
e.type === "touchend" ? e.changedTouches[0].clientX : e.clientX; |
|
|
|
|
|
|
|
const diff = dragStartX.current - endX; |
|
|
|
const diff = dragStartX.current - endX; |
|
|
|
if (diff > 50) next(); |
|
|
|
if (diff > 50) next(); |
|
|
|
else if (diff < -50) prev(); |
|
|
|
else if (diff < -50) prev(); |
|
|
|
@ -277,20 +258,10 @@ function SiPage() { |
|
|
|
<div className="si_archive__main"> |
|
|
|
<div className="si_archive__main"> |
|
|
|
{/* 헤더 */} |
|
|
|
{/* 헤더 */} |
|
|
|
<div className="si_archive__header"> |
|
|
|
<div className="si_archive__header"> |
|
|
|
<motion.span |
|
|
|
<motion.span className="fc-eyebrow" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, ease }}> |
|
|
|
className="fc-eyebrow" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
PROJECT ARCHIVE |
|
|
|
PROJECT ARCHIVE |
|
|
|
</motion.span> |
|
|
|
</motion.span> |
|
|
|
<motion.h2 |
|
|
|
<motion.h2 className="si_archive__title" initial={{ opacity: 0, y: 20 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.1, ease }}> |
|
|
|
className="si_archive__title" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.1, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{window.innerWidth <= 768 ? ( |
|
|
|
{window.innerWidth <= 768 ? ( |
|
|
|
"수행사업 아카이브" |
|
|
|
"수행사업 아카이브" |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
@ -301,12 +272,7 @@ function SiPage() { |
|
|
|
</> |
|
|
|
</> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</motion.h2> |
|
|
|
</motion.h2> |
|
|
|
<motion.p |
|
|
|
<motion.p className="si_archive__desc" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.2, ease }}> |
|
|
|
className="si_archive__desc" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.2, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{window.innerWidth <= 768 ? ( |
|
|
|
{window.innerWidth <= 768 ? ( |
|
|
|
"PAL Networks가 구축한 주요 프로젝트를 소개합니다." |
|
|
|
"PAL Networks가 구축한 주요 프로젝트를 소개합니다." |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
@ -320,40 +286,17 @@ function SiPage() { |
|
|
|
|
|
|
|
|
|
|
|
{/* 네비게이션 */} |
|
|
|
{/* 네비게이션 */} |
|
|
|
<div className="si_archive__nav"> |
|
|
|
<div className="si_archive__nav"> |
|
|
|
<motion.button |
|
|
|
<motion.button className="si_archive__nav-btn" onClick={prev} aria-label="이전" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.4, ease }}> |
|
|
|
className="si_archive__nav-btn" |
|
|
|
|
|
|
|
onClick={prev} |
|
|
|
|
|
|
|
aria-label="이전" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.4, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
← |
|
|
|
← |
|
|
|
</motion.button> |
|
|
|
</motion.button> |
|
|
|
|
|
|
|
|
|
|
|
<motion.div |
|
|
|
<motion.div className="si_archive__progress" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.4, ease }}> |
|
|
|
className="si_archive__progress" |
|
|
|
<span className="si_archive__progress-cur">{String(current + 1).padStart(2, "0")}</span> |
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.4, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<span className="si_archive__progress-cur"> |
|
|
|
|
|
|
|
{String(current + 1).padStart(2, "0")} |
|
|
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
<span className="si_archive__progress-divider">/</span> |
|
|
|
<span className="si_archive__progress-divider">/</span> |
|
|
|
<span className="si_archive__progress-total"> |
|
|
|
<span className="si_archive__progress-total">{String(total).padStart(2, "0")}</span> |
|
|
|
{String(total).padStart(2, "0")} |
|
|
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
</motion.div> |
|
|
|
</motion.div> |
|
|
|
|
|
|
|
|
|
|
|
<motion.button |
|
|
|
<motion.button className="si_archive__nav-btn" onClick={next} aria-label="다음" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.4, ease }}> |
|
|
|
className="si_archive__nav-btn" |
|
|
|
|
|
|
|
onClick={next} |
|
|
|
|
|
|
|
aria-label="다음" |
|
|
|
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.4, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
→ |
|
|
|
→ |
|
|
|
</motion.button> |
|
|
|
</motion.button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -361,56 +304,27 @@ function SiPage() { |
|
|
|
|
|
|
|
|
|
|
|
{/* 슬라이더 */} |
|
|
|
{/* 슬라이더 */} |
|
|
|
<div className="si_archive__right"> |
|
|
|
<div className="si_archive__right"> |
|
|
|
<motion.div |
|
|
|
<motion.div initial={{ opacity: 0, y: 30 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.3, ease }}> |
|
|
|
initial={{ opacity: 0, y: 30 }} |
|
|
|
<div className="si_archive__slider" style={{ display: "flex" }} onMouseDown={handleDragStart} onMouseUp={handleDragEnd} onTouchStart={handleDragStart} onTouchEnd={handleDragEnd} ref={sliderRef}> |
|
|
|
animate={inView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
|
|
|
transition={{ duration: 0.6, delay: 0.3, ease }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div |
|
|
|
|
|
|
|
className="si_archive__slider" |
|
|
|
|
|
|
|
style={{ display: "flex" }} |
|
|
|
|
|
|
|
onMouseDown={handleDragStart} |
|
|
|
|
|
|
|
onMouseUp={handleDragEnd} |
|
|
|
|
|
|
|
onTouchStart={handleDragStart} |
|
|
|
|
|
|
|
onTouchEnd={handleDragEnd} |
|
|
|
|
|
|
|
ref={sliderRef} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<motion.div |
|
|
|
<motion.div |
|
|
|
className="si_archive__track" |
|
|
|
className="si_archive__track" |
|
|
|
animate={{ |
|
|
|
animate={{ |
|
|
|
x: |
|
|
|
x: window.innerWidth <= 768 ? 0 : cardWidth ? -(current * (cardWidth + 18)) : 0, |
|
|
|
window.innerWidth <= 768 |
|
|
|
|
|
|
|
? 0 |
|
|
|
|
|
|
|
: cardWidth |
|
|
|
|
|
|
|
? -(current * (cardWidth + 18)) |
|
|
|
|
|
|
|
: 0, |
|
|
|
|
|
|
|
}} |
|
|
|
}} |
|
|
|
transition={{ duration: 0.55, ease }} |
|
|
|
transition={{ duration: 0.55, ease }} |
|
|
|
style={{ alignItems: "stretch" }} |
|
|
|
style={{ alignItems: "stretch" }} |
|
|
|
> |
|
|
|
> |
|
|
|
{PROJECTS.map((project, idx) => ( |
|
|
|
{PROJECTS.map((project, idx) => ( |
|
|
|
<div |
|
|
|
<div key={project.id} className="si_archive__card" ref={idx === 0 ? cardRef : null}> |
|
|
|
key={project.id} |
|
|
|
|
|
|
|
className="si_archive__card" |
|
|
|
|
|
|
|
ref={idx === 0 ? cardRef : null} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div className="si_archive__card-img"> |
|
|
|
<div className="si_archive__card-img"> |
|
|
|
<img |
|
|
|
<img src={`${basePath}images/si_img${parseInt(project.id)}.png`} alt={project.title} draggable="false" /> |
|
|
|
src={`${basePath}images/si_img${parseInt(project.id)}.png`} |
|
|
|
|
|
|
|
alt={project.title} |
|
|
|
|
|
|
|
draggable="false" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
<div className="si_archive__card-img-placeholder" /> |
|
|
|
<div className="si_archive__card-img-placeholder" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className="si_archive__card-body"> |
|
|
|
<div className="si_archive__card-body"> |
|
|
|
<div className="si_archive__card-header"> |
|
|
|
<div className="si_archive__card-header"> |
|
|
|
<div className="si_archive__card-num"> |
|
|
|
<div className="si_archive__card-num">{project.id}</div> |
|
|
|
{project.id} |
|
|
|
<h3 className="si_archive__card-title">{project.title}</h3> |
|
|
|
</div> |
|
|
|
|
|
|
|
<h3 className="si_archive__card-title"> |
|
|
|
|
|
|
|
{project.title} |
|
|
|
|
|
|
|
</h3> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className="si_archive__card-tags"> |
|
|
|
<div className="si_archive__card-tags"> |
|
|
|
@ -424,15 +338,9 @@ function SiPage() { |
|
|
|
<ul className="si_archive__card-desc"> |
|
|
|
<ul className="si_archive__card-desc"> |
|
|
|
{project.desc.map((item, i) => ( |
|
|
|
{project.desc.map((item, i) => ( |
|
|
|
<li key={i}> |
|
|
|
<li key={i}> |
|
|
|
<div className="si_archive__card-desc-icon"> |
|
|
|
<div className="si_archive__card-desc-icon">{item.icon}</div> |
|
|
|
{item.icon} |
|
|
|
<div className="si_archive__card-desc-title">{item.title}</div> |
|
|
|
</div> |
|
|
|
<div className="si_archive__card-desc-text">{item.text}</div> |
|
|
|
<div className="si_archive__card-desc-title"> |
|
|
|
|
|
|
|
{item.title} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="si_archive__card-desc-text"> |
|
|
|
|
|
|
|
{item.text} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</li> |
|
|
|
</li> |
|
|
|
))} |
|
|
|
))} |
|
|
|
</ul> |
|
|
|
</ul> |
|
|
|
|