|
|
|
@ -14,22 +14,18 @@ const COMPANY_NAV = [ |
|
|
|
{ label: "찾아오시는 길", to: "/company/location" }, |
|
|
|
{ label: "찾아오시는 길", to: "/company/location" }, |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
const CX = 380; |
|
|
|
|
|
|
|
const CY = 230; |
|
|
|
|
|
|
|
const ORBIT_R = 165; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const CLIENTS = [ |
|
|
|
const CLIENTS = [ |
|
|
|
{ id: "airport", logo: "01", angle: -90 }, |
|
|
|
{ id: "airport", logo: "01" }, |
|
|
|
{ id: "kac", logo: "09", angle: -45 }, |
|
|
|
{ id: "kac", logo: "09" }, |
|
|
|
{ id: "mod", logo: "15", angle: 0 }, |
|
|
|
{ id: "mod", logo: "15" }, |
|
|
|
{ id: "incheon", logo: "16", angle: 45 }, |
|
|
|
{ id: "incheon", logo: "16" }, |
|
|
|
{ id: "jeju", logo: "20", angle: 90 }, |
|
|
|
{ id: "jeju", logo: "20" }, |
|
|
|
{ id: "kiast", logo: "21", angle: 135 }, |
|
|
|
{ id: "kiast", logo: "21" }, |
|
|
|
{ id: "kari", logo: "23", angle: 180 }, |
|
|
|
{ id: "kari", logo: "23" }, |
|
|
|
{ id: "molit", logo: "24", angle: -135 }, |
|
|
|
{ id: "molit", logo: "24" }, |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
const PARTNER_LOGOS = [ |
|
|
|
const PARTNERS = [ |
|
|
|
"02", |
|
|
|
"02", |
|
|
|
"03", |
|
|
|
"03", |
|
|
|
"04", |
|
|
|
"04", |
|
|
|
@ -47,192 +43,26 @@ const PARTNER_LOGOS = [ |
|
|
|
"22", |
|
|
|
"22", |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
function toRad(deg) { |
|
|
|
function ClientCard({ logo, basePath }) { |
|
|
|
return (deg * Math.PI) / 180; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
function nodePos(angle) { |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
x: CX + ORBIT_R * Math.cos(toRad(angle)), |
|
|
|
|
|
|
|
y: CY + ORBIT_R * Math.sin(toRad(angle)), |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function ClientHub({ basePath }) { |
|
|
|
|
|
|
|
const svgRef = useRef(null); |
|
|
|
|
|
|
|
const orbitRef = useRef(null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
|
|
const ctx = gsap.context(() => { |
|
|
|
|
|
|
|
gsap.fromTo( |
|
|
|
|
|
|
|
".hub-edge", |
|
|
|
|
|
|
|
{ strokeDashoffset: 200, opacity: 0 }, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
strokeDashoffset: 0, |
|
|
|
|
|
|
|
opacity: 1, |
|
|
|
|
|
|
|
duration: 1.1, |
|
|
|
|
|
|
|
stagger: 0.07, |
|
|
|
|
|
|
|
ease: "power2.out", |
|
|
|
|
|
|
|
scrollTrigger: { trigger: svgRef.current, start: "top 75%" }, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
gsap.fromTo( |
|
|
|
|
|
|
|
".hub-center", |
|
|
|
|
|
|
|
{ scale: 0.6, opacity: 0, transformOrigin: `${CX}px ${CY}px` }, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
scale: 1, |
|
|
|
|
|
|
|
opacity: 1, |
|
|
|
|
|
|
|
duration: 0.7, |
|
|
|
|
|
|
|
ease: "back.out(1.6)", |
|
|
|
|
|
|
|
scrollTrigger: { trigger: svgRef.current, start: "top 80%" }, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
gsap.fromTo( |
|
|
|
|
|
|
|
".hub-node", |
|
|
|
|
|
|
|
{ scale: 0, opacity: 0 }, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
scale: 1, |
|
|
|
|
|
|
|
opacity: 1, |
|
|
|
|
|
|
|
duration: 0.55, |
|
|
|
|
|
|
|
stagger: 0.06, |
|
|
|
|
|
|
|
ease: "back.out(1.4)", |
|
|
|
|
|
|
|
scrollTrigger: { trigger: svgRef.current, start: "top 75%" }, |
|
|
|
|
|
|
|
delay: 0.35, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gsap.to(orbitRef.current, { |
|
|
|
|
|
|
|
rotation: 360, |
|
|
|
|
|
|
|
svgOrigin: `${CX} ${CY}`, |
|
|
|
|
|
|
|
duration: 30, |
|
|
|
|
|
|
|
ease: "none", |
|
|
|
|
|
|
|
repeat: -1, |
|
|
|
|
|
|
|
paused: true, // 추가 |
|
|
|
|
|
|
|
scrollTrigger: { |
|
|
|
|
|
|
|
trigger: svgRef.current, |
|
|
|
|
|
|
|
start: "top 75%", |
|
|
|
|
|
|
|
toggleActions: "play pause resume pause", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, svgRef); |
|
|
|
|
|
|
|
return () => ctx.revert(); |
|
|
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<svg |
|
|
|
<div className="partner-card"> |
|
|
|
ref={svgRef} |
|
|
|
<img |
|
|
|
className="client-hub-svg" |
|
|
|
src={`${basePath}images/partner/banner${logo}.png`} |
|
|
|
viewBox="0 0 760 480" |
|
|
|
alt="" |
|
|
|
xmlns="http://www.w3.org/2000/svg" |
|
|
|
loading="lazy" |
|
|
|
> |
|
|
|
|
|
|
|
<circle |
|
|
|
|
|
|
|
ref={orbitRef} |
|
|
|
|
|
|
|
cx={CX} |
|
|
|
|
|
|
|
cy={CY} |
|
|
|
|
|
|
|
r={ORBIT_R} |
|
|
|
|
|
|
|
fill="none" |
|
|
|
|
|
|
|
stroke="#e4e4ec" |
|
|
|
|
|
|
|
strokeWidth="1" |
|
|
|
|
|
|
|
strokeDasharray="5 7" |
|
|
|
|
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
</div> |
|
|
|
{CLIENTS.map((c) => { |
|
|
|
|
|
|
|
const pos = nodePos(c.angle); |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<line |
|
|
|
|
|
|
|
key={c.id} |
|
|
|
|
|
|
|
className="hub-edge" |
|
|
|
|
|
|
|
x1={CX} |
|
|
|
|
|
|
|
y1={CY} |
|
|
|
|
|
|
|
x2={pos.x} |
|
|
|
|
|
|
|
y2={pos.y} |
|
|
|
|
|
|
|
stroke="#d8d8e4" |
|
|
|
|
|
|
|
strokeWidth="1" |
|
|
|
|
|
|
|
strokeDasharray="200" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<g className="hub-center"> |
|
|
|
|
|
|
|
<circle cx={CX} cy={CY} r={44} fill="#1e2d4f" opacity="1" /> |
|
|
|
|
|
|
|
<circle |
|
|
|
|
|
|
|
cx={CX} |
|
|
|
|
|
|
|
cy={CY} |
|
|
|
|
|
|
|
r={44} |
|
|
|
|
|
|
|
fill="none" |
|
|
|
|
|
|
|
stroke="#3a4f7a" |
|
|
|
|
|
|
|
strokeWidth="1.2" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
<text |
|
|
|
|
|
|
|
x={CX} |
|
|
|
|
|
|
|
y={CY - 9} |
|
|
|
|
|
|
|
textAnchor="middle" |
|
|
|
|
|
|
|
fill="#e8eaf0" |
|
|
|
|
|
|
|
fontSize="12" |
|
|
|
|
|
|
|
fontWeight="500" |
|
|
|
|
|
|
|
letterSpacing="0.04em" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
주요 |
|
|
|
|
|
|
|
</text> |
|
|
|
|
|
|
|
<text |
|
|
|
|
|
|
|
x={CX} |
|
|
|
|
|
|
|
y={CY + 7} |
|
|
|
|
|
|
|
textAnchor="middle" |
|
|
|
|
|
|
|
fill="#e8eaf0" |
|
|
|
|
|
|
|
fontSize="12" |
|
|
|
|
|
|
|
fontWeight="500" |
|
|
|
|
|
|
|
letterSpacing="0.04em" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
고객사 |
|
|
|
|
|
|
|
</text> |
|
|
|
|
|
|
|
<text |
|
|
|
|
|
|
|
x={CX} |
|
|
|
|
|
|
|
y={CY + 23} |
|
|
|
|
|
|
|
textAnchor="middle" |
|
|
|
|
|
|
|
fill="#8892b0" |
|
|
|
|
|
|
|
fontSize="10" |
|
|
|
|
|
|
|
fontWeight="500" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
8 clients |
|
|
|
|
|
|
|
</text> |
|
|
|
|
|
|
|
</g> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{CLIENTS.map((c) => { |
|
|
|
|
|
|
|
const pos = nodePos(c.angle); |
|
|
|
|
|
|
|
const imgSrc = `${basePath}images/partner/banner${c.logo}.png`; |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<g |
|
|
|
|
|
|
|
key={c.id} |
|
|
|
|
|
|
|
className="hub-node" |
|
|
|
|
|
|
|
style={{ transformOrigin: `${pos.x}px ${pos.y}px` }} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<circle |
|
|
|
|
|
|
|
cx={pos.x} |
|
|
|
|
|
|
|
cy={pos.y} |
|
|
|
|
|
|
|
r={30} |
|
|
|
|
|
|
|
fill="#f4f5f9" |
|
|
|
|
|
|
|
stroke="#B8BCCF" |
|
|
|
|
|
|
|
strokeWidth="1.5" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
<image |
|
|
|
|
|
|
|
href={imgSrc} |
|
|
|
|
|
|
|
x={pos.x - 22} |
|
|
|
|
|
|
|
y={pos.y - 22} |
|
|
|
|
|
|
|
width="44" |
|
|
|
|
|
|
|
height="44" |
|
|
|
|
|
|
|
preserveAspectRatio="xMidYMid meet" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</g> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
</svg> |
|
|
|
|
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function LogoItem({ num, basePath }) { |
|
|
|
function PartnerCard({ logo, basePath }) { |
|
|
|
const src = `${basePath}images/partner/banner${num}.png`; |
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="partner-logo-card"> |
|
|
|
<div className="partner-card"> |
|
|
|
<img src={src} alt="" loading="lazy" /> |
|
|
|
<img |
|
|
|
|
|
|
|
src={`${basePath}images/partner/banner${logo}.png`} |
|
|
|
|
|
|
|
alt="" |
|
|
|
|
|
|
|
loading="lazy" |
|
|
|
|
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -245,22 +75,16 @@ export default function PartnersPage() { |
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
const ctx = gsap.context(() => { |
|
|
|
const ctx = gsap.context(() => { |
|
|
|
gsap.fromTo( |
|
|
|
gsap.fromTo( |
|
|
|
".partner-logo-card", |
|
|
|
".partner-card", |
|
|
|
{ |
|
|
|
{ opacity: 0, y: 20, scale: 0.97 }, |
|
|
|
opacity: 0, |
|
|
|
|
|
|
|
y: 36, |
|
|
|
|
|
|
|
scale: 0.96, |
|
|
|
|
|
|
|
clipPath: "inset(0 100% 0 0 round 0px)", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
opacity: 1, |
|
|
|
opacity: 1, |
|
|
|
y: 0, |
|
|
|
y: 0, |
|
|
|
scale: 1, |
|
|
|
scale: 1, |
|
|
|
clipPath: "inset(0 0% 0 0 round 0px)", |
|
|
|
duration: 0.6, |
|
|
|
duration: 0.85, |
|
|
|
|
|
|
|
stagger: { each: 0.04, from: "start" }, |
|
|
|
stagger: { each: 0.04, from: "start" }, |
|
|
|
ease: "power3.out", |
|
|
|
ease: "power3.out", |
|
|
|
scrollTrigger: { trigger: ".partner-logo-grid", start: "top 78%" }, |
|
|
|
scrollTrigger: { trigger: ".partners-wrap", start: "top 80%" }, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
); |
|
|
|
}, editorialRef); |
|
|
|
}, editorialRef); |
|
|
|
@ -282,43 +106,45 @@ export default function PartnersPage() { |
|
|
|
navItems={COMPANY_NAV} |
|
|
|
navItems={COMPANY_NAV} |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<div className="partner-page" ref={editorialRef}> |
|
|
|
<div className="partners-wrap" ref={editorialRef}> |
|
|
|
<div className="partner-editorial"> |
|
|
|
{/* 주요 고객사 */} |
|
|
|
{/* 주요 고객사 */} |
|
|
|
<section className="partners-section"> |
|
|
|
<section className="partner-hub-section"> |
|
|
|
<p className="partners-eyebrow">Clients</p> |
|
|
|
<div className="partner-section-heading"> |
|
|
|
<h2 className="partners-title">주요 고객사</h2> |
|
|
|
<h2 className="partner-section-title"> |
|
|
|
<div className="partners-divider" /> |
|
|
|
함께 만들어온 |
|
|
|
<div className="partners-grid-8"> |
|
|
|
<br /> |
|
|
|
{CLIENTS.map((c) => ( |
|
|
|
신뢰의 네트워크 |
|
|
|
<ClientCard key={c.id} logo={c.logo} basePath={basePath} /> |
|
|
|
</h2> |
|
|
|
))} |
|
|
|
<p className="partner-section-sub"> |
|
|
|
</div> |
|
|
|
공공·항공·국방 분야 핵심 기관과 오랜 협력 관계를 이어오고 |
|
|
|
</section> |
|
|
|
있습니다 |
|
|
|
|
|
|
|
</p> |
|
|
|
{/* 기술 협력사 */} |
|
|
|
</div> |
|
|
|
<section className="partners-section"> |
|
|
|
<ClientHub basePath={basePath} /> |
|
|
|
<p className="partners-eyebrow">Partners</p> |
|
|
|
</section> |
|
|
|
<h2 className="partners-title">기술 협력사</h2> |
|
|
|
|
|
|
|
<div className="partners-divider" /> |
|
|
|
{/* 기술 협력사 */} |
|
|
|
<div className="partners-grid-15"> |
|
|
|
<section className="partner-logo-section"> |
|
|
|
{PARTNERS.map((num) => ( |
|
|
|
<div className="partner-logo-top"> |
|
|
|
<PartnerCard key={num} logo={num} basePath={basePath} /> |
|
|
|
<div> |
|
|
|
))} |
|
|
|
<span className="partner-logo-kicker">Partners</span> |
|
|
|
</div> |
|
|
|
<h2 className="partner-section-title">기술 협력사</h2> |
|
|
|
</section> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="partner-logo-stat"> |
|
|
|
{/* CTA */} |
|
|
|
<span className="partner-logo-stat__num">15</span> |
|
|
|
<section className="partners-cta"> |
|
|
|
<span className="partner-logo-stat__label">협력사</span> |
|
|
|
<div> |
|
|
|
</div> |
|
|
|
<p className="partners-cta-label">Become a Partner</p> |
|
|
|
</div> |
|
|
|
<h3 className="partners-cta-title"> |
|
|
|
<div className="partner-logo-grid partner-logo-grid--partners"> |
|
|
|
팔네트웍스와 함께 성장할 |
|
|
|
{PARTNER_LOGOS.map((num) => ( |
|
|
|
<br /> |
|
|
|
<LogoItem key={num} num={num} basePath={basePath} /> |
|
|
|
파트너를 찾습니다 |
|
|
|
))} |
|
|
|
</h3> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
<a href="/contact/inquiry" className="partners-cta-btn"> |
|
|
|
</div> |
|
|
|
협력 문의하기 |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
</section> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</article> |
|
|
|
</article> |
|
|
|
); |
|
|
|
); |
|
|
|
|