|
|
|
|
@ -105,7 +105,8 @@ function NetworkGlobe() {
|
|
|
|
|
const d = Math.sqrt(dx * dx + dy * dy + dz * dz); |
|
|
|
|
if (d < 0.55) { |
|
|
|
|
const depth = (a.sz + b.sz) * 0.5; |
|
|
|
|
const alpha = Math.max(0, 0.05 + (depth + 1) * 0.1) * (1 - d / 0.55); |
|
|
|
|
const alpha = |
|
|
|
|
Math.max(0, 0.05 + (depth + 1) * 0.1) * (1 - d / 0.55); |
|
|
|
|
const aHex = Math.round(Math.min(255, alpha * 255)) |
|
|
|
|
.toString(16) |
|
|
|
|
.padStart(2, "0"); |
|
|
|
|
@ -134,7 +135,14 @@ function NetworkGlobe() {
|
|
|
|
|
.toString(16) |
|
|
|
|
.padStart(2, "0"); |
|
|
|
|
|
|
|
|
|
const glow = ctx.createRadialGradient(n.sx, n.sy, 0, n.sx, n.sy, r * 3.5); |
|
|
|
|
const glow = ctx.createRadialGradient( |
|
|
|
|
n.sx, |
|
|
|
|
n.sy, |
|
|
|
|
0, |
|
|
|
|
n.sx, |
|
|
|
|
n.sy, |
|
|
|
|
r * 3.5, |
|
|
|
|
); |
|
|
|
|
glow.addColorStop(0, n.color + gHex); |
|
|
|
|
glow.addColorStop(1, n.color + "00"); |
|
|
|
|
ctx.beginPath(); |
|
|
|
|
@ -172,7 +180,12 @@ function NetworkGlobe() {
|
|
|
|
|
function TitleLine({ children, delay }) { |
|
|
|
|
return ( |
|
|
|
|
<span className="sh4-title-line"> |
|
|
|
|
<motion.span className="sh4-title-line-inner" initial={{ y: "105%" }} animate={{ y: "0%" }} transition={{ duration: 1.1, delay, ease: [0.16, 1, 0.3, 1] }}> |
|
|
|
|
<motion.span |
|
|
|
|
className="sh4-title-line-inner" |
|
|
|
|
initial={{ y: "105%" }} |
|
|
|
|
animate={{ y: "0%" }} |
|
|
|
|
transition={{ duration: 1.1, delay, ease: [0.16, 1, 0.3, 1] }} |
|
|
|
|
> |
|
|
|
|
{children} |
|
|
|
|
</motion.span> |
|
|
|
|
</span> |
|
|
|
|
@ -202,7 +215,9 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
|
|
|
|
|
)) |
|
|
|
|
: // JSX: <br/>로 나뉜 형태 → 직접 TitleLine 배열로 변환 |
|
|
|
|
(() => { |
|
|
|
|
const children = Array.isArray(title.props?.children) ? title.props.children : [title]; |
|
|
|
|
const children = Array.isArray(title.props?.children) |
|
|
|
|
? title.props.children |
|
|
|
|
: [title]; |
|
|
|
|
const lines = []; |
|
|
|
|
let current = []; |
|
|
|
|
children.forEach((child, i) => { |
|
|
|
|
@ -229,21 +244,39 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
|
|
|
|
|
<div className="sh4-inner"> |
|
|
|
|
<div className="sh4-left"> |
|
|
|
|
{/* // 브레드크럼 */} |
|
|
|
|
<motion.nav className="sh4-breadcrumb" initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} aria-label="breadcrumb"> |
|
|
|
|
<motion.nav |
|
|
|
|
className="sh4-breadcrumb" |
|
|
|
|
initial={{ opacity: 0, y: 8 }} |
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} |
|
|
|
|
aria-label="breadcrumb" |
|
|
|
|
> |
|
|
|
|
<Link to="/" className="sh4-breadcrumb-item"> |
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
|
|
|
|
<svg |
|
|
|
|
width="14" |
|
|
|
|
height="14" |
|
|
|
|
viewBox="0 0 24 24" |
|
|
|
|
fill="none" |
|
|
|
|
stroke="currentColor" |
|
|
|
|
strokeWidth="2" |
|
|
|
|
> |
|
|
|
|
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z" /> |
|
|
|
|
<polyline points="9 22 9 12 15 12 15 22" /> |
|
|
|
|
</svg> |
|
|
|
|
</Link> |
|
|
|
|
<span className="sh4-breadcrumb-sep">/</span> |
|
|
|
|
<Link to={"/" + pathname.split("/")[1]} className="sh4-breadcrumb-item"> |
|
|
|
|
<Link |
|
|
|
|
to={"/" + pathname.split("/")[1]} |
|
|
|
|
className="sh4-breadcrumb-item" |
|
|
|
|
> |
|
|
|
|
{menuMap["/" + pathname.split("/")[1]]?.label} |
|
|
|
|
</Link> |
|
|
|
|
{pathname.split("/")[2] && ( |
|
|
|
|
<> |
|
|
|
|
<span className="sh4-breadcrumb-sep">/</span> |
|
|
|
|
<span className="sh4-breadcrumb-item sh4-breadcrumb-item--active">{navItems?.find((n) => n.to === pathname)?.label}</span> |
|
|
|
|
<span className="sh4-breadcrumb-item sh4-breadcrumb-item--active"> |
|
|
|
|
{navItems?.find((n) => n.to === pathname)?.label} |
|
|
|
|
</span> |
|
|
|
|
</> |
|
|
|
|
)} |
|
|
|
|
</motion.nav> |
|
|
|
|
@ -255,7 +288,11 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
|
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
transition={{ |
|
|
|
|
duration: 0.6, |
|
|
|
|
delay: 0.1 + (typeof title === "string" ? title.split("\n").length : 2) * 0.1 + 0.15, |
|
|
|
|
delay: |
|
|
|
|
0.1 + |
|
|
|
|
(typeof title === "string" ? title.split("\n").length : 2) * |
|
|
|
|
0.1 + |
|
|
|
|
0.15, |
|
|
|
|
ease: [0.16, 1, 0.3, 1], |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
@ -265,7 +302,12 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
{rightSlot && ( |
|
|
|
|
<motion.div className="sh4-right" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 1, delay: 0.3 }}> |
|
|
|
|
<motion.div |
|
|
|
|
className="sh4-right" |
|
|
|
|
initial={{ opacity: 0 }} |
|
|
|
|
animate={{ opacity: 1 }} |
|
|
|
|
transition={{ duration: 1, delay: 0.3 }} |
|
|
|
|
> |
|
|
|
|
{rightSlot} |
|
|
|
|
</motion.div> |
|
|
|
|
)} |
|
|
|
|
@ -273,10 +315,18 @@ export default function SubHero({ title, desc, navItems, rightSlot }) {
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
{navItems?.length > 1 && ( |
|
|
|
|
<nav ref={navRef} className={`sh4-nav-wrap${isPill ? " is-pill" : ""}`} aria-label="Sub Navigation"> |
|
|
|
|
<nav |
|
|
|
|
ref={navRef} |
|
|
|
|
className={`sh4-nav-wrap${isPill ? " is-pill" : ""}`} |
|
|
|
|
aria-label="Sub Navigation" |
|
|
|
|
> |
|
|
|
|
<div className="sh4-nav"> |
|
|
|
|
{navItems.map((item) => ( |
|
|
|
|
<Link key={item.to} to={item.to} className={`sh4-nav-tab${pathname === item.to ? " sh4-nav-tab--active" : ""}`}> |
|
|
|
|
<Link |
|
|
|
|
key={item.to} |
|
|
|
|
to={item.to} |
|
|
|
|
className={`sh4-nav-tab${pathname === item.to ? " sh4-nav-tab--active" : ""}`} |
|
|
|
|
> |
|
|
|
|
{item.label} |
|
|
|
|
</Link> |
|
|
|
|
))} |
|
|
|
|
|