fix(background): resolve landscape right-side collision, tailor portrait layout & increase mouse travel
This commit is contained in:
1 parent
3b7425c5b6
commit
5d90407a41
2 files changed
+137
-201
No files matched your search
@@ -12,10 +12,12 @@
|
||||
- redesign the card of certificates large picture, badge/cert code, issuing org, and title only -> hover and when click show all the information of the certificate in a modal -> dont make verify button only on credly or other platform
|
||||
- in about page it has certifications section -> remove it because it is already in certifications page -->
|
||||
|
||||
- in about section bento cell 2 make the image optimize load [About.tsx#L99-103](textBlock;file:///f%3A/WebDevelopment/Portfolio/src/components/About.tsx#L99-103)
|
||||
<!-- - in about section bento cell 2 make the image optimize load [About.tsx#L99-103](textBlock;file:///f%3A/WebDevelopment/Portfolio/src/components/About.tsx#L99-103)
|
||||
- link page cv url /CV-Cris-Charles-Garcia.pdf -> available work chip fetch from sanity same from home page
|
||||
- in cards of certificate/badges can be previewed if the picture is a certificate not a badge so make it large and can be previewed
|
||||
- certifications page title remove the ```03 // Credentials & Badges
|
||||
|
||||
Certifications & Badges
|
||||
Verified digital credentials from Cisco and industry partners. Each badge represents completed coursework, assessments, and demonstrated competency.``` section
|
||||
Verified digital credentials from Cisco and industry partners. Each badge represents completed coursework, assessments, and demonstrated competency.``` section -->
|
||||
<!-- - background shapes in landscape the right side shapes collides
|
||||
- in portrait make the different approach of parallex and layout of the shapes -->
|
||||
+133
-199
@@ -3,74 +3,104 @@ import { motion, useScroll, useTransform, useSpring } from 'framer-motion';
|
||||
|
||||
export default function Background() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [isPortrait, setIsPortrait] = useState(false);
|
||||
const [reducedMotion, setReducedMotion] = useState(false);
|
||||
|
||||
// Smooth mouse inertia on desktop
|
||||
const mouseX = useSpring(0, { stiffness: 40, damping: 24 });
|
||||
const mouseY = useSpring(0, { stiffness: 40, damping: 24 });
|
||||
// Smooth mouse inertia on desktop (disabled on touch/portrait)
|
||||
const mouseX = useSpring(0, { stiffness: 35, damping: 25 });
|
||||
const mouseY = useSpring(0, { stiffness: 35, damping: 25 });
|
||||
|
||||
// Native window scroll tracking
|
||||
const { scrollY } = useScroll();
|
||||
|
||||
// ─── Layer 1: Dot Matrix Grid ───
|
||||
const gridY = useTransform(scrollY, [0, 3000], [0, 100]);
|
||||
const gridY = useTransform(scrollY, [0, 3000], [0, 90]);
|
||||
|
||||
// ─── Unified Direction Movement (Prevents any collision during scroll) ───
|
||||
// All shapes drift in the same cohesive direction at gentle depth offsets
|
||||
const shape1ScrollY = useTransform(scrollY, [0, 3000], [0, 160]);
|
||||
const shape1ScrollX = useTransform(scrollY, [0, 3000], [0, 30]);
|
||||
const shape1Rotate = useTransform(scrollY, [0, 3000], [0, 12]);
|
||||
// Shape 1: Zooms in a little as you scroll
|
||||
const shape1Scale = useTransform(scrollY, [0, 1500, 3000], [0.95, 1.08, 1.0]);
|
||||
// ─── LANDSCAPE PARALLAX MECHANICS ───
|
||||
// 1. Controlled vertical drift in the same cohesive direction
|
||||
const lsShape1Y = useTransform(scrollY, [0, 3000], [0, 140]);
|
||||
const lsShape1X = useTransform(scrollY, [0, 3000], [0, 25]);
|
||||
const lsShape1Rotate = useTransform(scrollY, [0, 3000], [0, 15]);
|
||||
const lsShape1Scale = useTransform(scrollY, [0, 1500, 3000], [0.95, 1.06, 1.0]);
|
||||
|
||||
const shape2ScrollY = useTransform(scrollY, [0, 3000], [0, 120]);
|
||||
const shape2ScrollX = useTransform(scrollY, [0, 3000], [0, -25]);
|
||||
const shape2Rotate = useTransform(scrollY, [0, 3000], [0, -15]);
|
||||
// Shape 2: Zooms out a little as you scroll
|
||||
const shape2Scale = useTransform(scrollY, [0, 1500, 3000], [1.06, 0.92, 0.98]);
|
||||
// Shape 2 (Upper Right - Cisco Topology)
|
||||
const lsShape2Y = useTransform(scrollY, [0, 3000], [0, 110]);
|
||||
const lsShape2X = useTransform(scrollY, [0, 3000], [0, -20]);
|
||||
const lsShape2Rotate = useTransform(scrollY, [0, 3000], [0, -15]);
|
||||
const lsShape2Scale = useTransform(scrollY, [0, 1500, 3000], [1.04, 0.94, 0.98]);
|
||||
// Landscape Right-Side Collision Fix: Shape 2 fades out as you reach the lower half
|
||||
const lsShape2Opacity = useTransform(scrollY, [0, 1000, 1700], [1, 0.9, 0]);
|
||||
|
||||
const shape3ScrollY = useTransform(scrollY, [0, 3000], [0, 140]);
|
||||
const shape3ScrollX = useTransform(scrollY, [0, 3000], [0, 30]);
|
||||
const shape3Rotate = useTransform(scrollY, [0, 3000], [-5, 10]);
|
||||
// Shape 3: Zooms in a little as you scroll past it
|
||||
const shape3Scale = useTransform(scrollY, [0, 1500, 3000], [0.92, 1.07, 1.0]);
|
||||
// Shape 3 (Lower-Mid Left - Terminal Window)
|
||||
const lsShape3Y = useTransform(scrollY, [0, 3000], [0, 130]);
|
||||
const lsShape3X = useTransform(scrollY, [0, 3000], [0, 25]);
|
||||
const lsShape3Rotate = useTransform(scrollY, [0, 3000], [-5, 12]);
|
||||
const lsShape3Scale = useTransform(scrollY, [0, 1500, 3000], [0.93, 1.05, 1.0]);
|
||||
|
||||
const shape4ScrollY = useTransform(scrollY, [0, 3000], [0, 110]);
|
||||
const shape4ScrollX = useTransform(scrollY, [0, 3000], [0, -25]);
|
||||
const shape4Rotate = useTransform(scrollY, [0, 3000], [0, 15]);
|
||||
// Shape 4: Zooms out a little
|
||||
const shape4Scale = useTransform(scrollY, [0, 1500, 3000], [1.05, 0.93, 1.02]);
|
||||
// Shape 4 (Lower Right - Git Braces)
|
||||
const lsShape4Y = useTransform(scrollY, [0, 3000], [0, 100]);
|
||||
const lsShape4X = useTransform(scrollY, [0, 3000], [0, -20]);
|
||||
const lsShape4Rotate = useTransform(scrollY, [0, 3000], [0, 15]);
|
||||
const lsShape4Scale = useTransform(scrollY, [0, 1500, 3000], [0.94, 1.04, 1.0]);
|
||||
// Landscape Right-Side Collision Fix: Shape 4 fades in only when entering the lower half
|
||||
const lsShape4Opacity = useTransform(scrollY, [700, 1400, 3000], [0, 0.9, 1]);
|
||||
|
||||
// Unified Scroll + Mouse transforms (prevents Framer Motion transform collisions)
|
||||
const shape1FinalX = useTransform([shape1ScrollX, mouseX], ([sX, mX]) => (sX as number) + (mX as number));
|
||||
const shape1FinalY = useTransform([shape1ScrollY, mouseY], ([sY, mY]) => (sY as number) + (mY as number));
|
||||
// Combined mouse tracking for desktop landscape with increased travel
|
||||
const lsShape1FinalX = useTransform([lsShape1X, mouseX], ([sX, mX]) => (sX as number) + (mX as number) * 1.2);
|
||||
const lsShape1FinalY = useTransform([lsShape1Y, mouseY], ([sY, mY]) => (sY as number) + (mY as number) * 1.2);
|
||||
|
||||
const shape2FinalX = useTransform([shape2ScrollX, mouseX], ([sX, mX]) => (sX as number) - (mX as number) * 1.1);
|
||||
const shape2FinalY = useTransform([shape2ScrollY, mouseY], ([sY, mY]) => (sY as number) - (mY as number) * 1.1);
|
||||
const lsShape2FinalX = useTransform([lsShape2X, mouseX], ([sX, mX]) => (sX as number) - (mX as number) * 1.3);
|
||||
const lsShape2FinalY = useTransform([lsShape2Y, mouseY], ([sY, mY]) => (sY as number) - (mY as number) * 1.3);
|
||||
|
||||
const shape3FinalX = useTransform([shape3ScrollX, mouseX], ([sX, mX]) => (sX as number) + (mX as number) * 0.8);
|
||||
const shape3FinalY = useTransform([shape3ScrollY, mouseY], ([sY, mY]) => (sY as number) + (mY as number) * 0.8);
|
||||
const lsShape3FinalX = useTransform([lsShape3X, mouseX], ([sX, mX]) => (sX as number) + (mX as number) * 1.1);
|
||||
const lsShape3FinalY = useTransform([lsShape3Y, mouseY], ([sY, mY]) => (sY as number) + (mY as number) * 1.1);
|
||||
|
||||
const shape4FinalX = useTransform([shape4ScrollX, mouseX], ([sX, mX]) => (sX as number) - (mX as number) * 0.9);
|
||||
const shape4FinalY = useTransform([shape4ScrollY, mouseY], ([sY, mY]) => (sY as number) + (mY as number) * 0.9);
|
||||
const lsShape4FinalX = useTransform([lsShape4X, mouseX], ([sX, mX]) => (sX as number) - (mX as number) * 1.2);
|
||||
const lsShape4FinalY = useTransform([lsShape4Y, mouseY], ([sY, mY]) => (sY as number) + (mY as number) * 1.2);
|
||||
|
||||
// ─── PORTRAIT PARALLAX MECHANICS (MOBILE & VERTICAL TABLET) ───
|
||||
// In portrait, shapes follow a vertical spine watermark approach with section focus
|
||||
const ptShape1Y = useTransform(scrollY, [0, 1200], [0, 90]);
|
||||
const ptShape1Opacity = useTransform(scrollY, [0, 600, 1200], [0.9, 0.5, 0.1]);
|
||||
|
||||
const ptShape2Y = useTransform(scrollY, [400, 1800], [-40, 70]);
|
||||
const ptShape2Opacity = useTransform(scrollY, [300, 900, 1600], [0.1, 0.85, 0.15]);
|
||||
|
||||
const ptShape3Y = useTransform(scrollY, [1200, 2600], [-40, 70]);
|
||||
const ptShape3Opacity = useTransform(scrollY, [1100, 1800, 2400], [0.1, 0.85, 0.15]);
|
||||
|
||||
const ptShape4Y = useTransform(scrollY, [2000, 3200], [-30, 60]);
|
||||
const ptShape4Opacity = useTransform(scrollY, [1800, 2500, 3200], [0.1, 0.9, 0.9]);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
|
||||
const checkOrientation = () => {
|
||||
setIsPortrait(window.innerHeight > window.innerWidth);
|
||||
};
|
||||
checkOrientation();
|
||||
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
setReducedMotion(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const centerX = window.innerWidth / 2;
|
||||
const centerY = window.innerHeight / 2;
|
||||
mouseX.set((e.clientX - centerX) * 0.04);
|
||||
mouseY.set((e.clientY - centerY) * 0.04);
|
||||
// Only track mouse in landscape desktop
|
||||
if (window.innerHeight <= window.innerWidth) {
|
||||
const centerX = window.innerWidth / 2;
|
||||
const centerY = window.innerHeight / 2;
|
||||
mouseX.set((e.clientX - centerX) * 0.065);
|
||||
mouseY.set((e.clientY - centerY) * 0.065);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', checkOrientation);
|
||||
window.addEventListener('mousemove', handleMouseMove, { passive: true });
|
||||
return () => window.removeEventListener('mousemove', handleMouseMove);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkOrientation);
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
}, [mouseX, mouseY]);
|
||||
|
||||
if (!mounted) return null;
|
||||
@@ -80,22 +110,28 @@ export default function Background() {
|
||||
{/* ─── Layer 1: Dot Matrix Grid (Code Matrix) ─── */}
|
||||
<motion.div
|
||||
style={reducedMotion ? {} : { y: gridY }}
|
||||
className="absolute -top-32 -bottom-32 inset-x-0 bg-[radial-gradient(hsla(var(--foreground)/0.12)_1px,transparent_1px)] [background-size:24px_24px] [mask-image:radial-gradient(ellipse_80%_80%_at_50%_50%,#000_60%,transparent_100%)] opacity-40 will-change-transform"
|
||||
className="absolute -top-32 -bottom-32 inset-x-0 bg-[radial-gradient(hsla(var(--foreground)/0.12)_1px,transparent_1px)] [background-size:24px_24px] [mask-image:radial-gradient(ellipse_80%_80%_at_50%_50%,#000_60%,transparent_100%)] opacity-35 will-change-transform"
|
||||
/>
|
||||
|
||||
{/* ─── 1. Top-Left: Stylized Code Brackets & Tag </> (Zooms in subtly on scroll) ─── */}
|
||||
{/* ─── 1. Code Tag </> (Top-Left in Landscape / Center Crest in Portrait) ─── */}
|
||||
<motion.div
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: isPortrait
|
||||
? { y: ptShape1Y, opacity: ptShape1Opacity }
|
||||
: {
|
||||
x: shape1FinalX,
|
||||
y: shape1FinalY,
|
||||
rotate: shape1Rotate,
|
||||
scale: shape1Scale,
|
||||
x: lsShape1FinalX,
|
||||
y: lsShape1FinalY,
|
||||
rotate: lsShape1Rotate,
|
||||
scale: lsShape1Scale,
|
||||
}
|
||||
}
|
||||
className="absolute top-[3%] left-[4%] sm:left-[10%] w-[360px] sm:w-[460px] h-[360px] sm:h-[460px] will-change-transform"
|
||||
className={
|
||||
isPortrait
|
||||
? 'absolute top-[4%] left-1/2 -translate-x-1/2 w-[270px] h-[270px] will-change-transform'
|
||||
: 'absolute top-[4%] left-[3%] sm:left-[8%] w-[320px] lg:w-[410px] h-[320px] lg:h-[410px] will-change-transform'
|
||||
}
|
||||
>
|
||||
<svg viewBox="0 0 500 500" className="w-full h-full drop-shadow-xs">
|
||||
<defs>
|
||||
@@ -104,7 +140,6 @@ export default function Background() {
|
||||
<stop offset="100%" stopColor="var(--secondary-color)" stopOpacity="0.02" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
{/* Background Outer Hex / Diamond Frame */}
|
||||
<polygon
|
||||
points="250,30 450,140 450,360 250,470 50,360 50,140"
|
||||
fill="url(#codeGrad)"
|
||||
@@ -112,80 +147,38 @@ export default function Background() {
|
||||
strokeOpacity="0.18"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
{/* Inner Dashed Tech Ring */}
|
||||
<circle
|
||||
cx="250"
|
||||
cy="250"
|
||||
r="170"
|
||||
fill="none"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.10"
|
||||
strokeWidth="1"
|
||||
strokeDasharray="6 6"
|
||||
/>
|
||||
{/* Left Angle Bracket < */}
|
||||
<path
|
||||
d="M 190 170 L 110 250 L 190 330"
|
||||
fill="none"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.30"
|
||||
strokeWidth="3.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
{/* Center Forward Slash / */}
|
||||
<line
|
||||
x1="275"
|
||||
y1="160"
|
||||
x2="225"
|
||||
y2="340"
|
||||
stroke="var(--secondary-color)"
|
||||
strokeOpacity="0.28"
|
||||
strokeWidth="3.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
{/* Right Angle Bracket > */}
|
||||
<path
|
||||
d="M 310 170 L 390 250 L 310 330"
|
||||
fill="none"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.30"
|
||||
strokeWidth="3.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
{/* Subtle Dev Watermark text */}
|
||||
<text
|
||||
x="250"
|
||||
y="410"
|
||||
textAnchor="middle"
|
||||
fill="var(--primary-color)"
|
||||
fillOpacity="0.25"
|
||||
fontSize="13"
|
||||
fontFamily="var(--font-mono)"
|
||||
letterSpacing="6"
|
||||
>
|
||||
<circle cx="250" cy="250" r="170" fill="none" stroke="var(--primary-color)" strokeOpacity="0.10" strokeWidth="1" strokeDasharray="6 6" />
|
||||
<path d="M 190 170 L 110 250 L 190 330" fill="none" stroke="var(--primary-color)" strokeOpacity="0.28" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<line x1="275" y1="160" x2="225" y2="340" stroke="var(--secondary-color)" strokeOpacity="0.26" strokeWidth="3.2" strokeLinecap="round" />
|
||||
<path d="M 310 170 L 390 250 L 310 330" fill="none" stroke="var(--primary-color)" strokeOpacity="0.28" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<text x="250" y="410" textAnchor="middle" fill="var(--primary-color)" fillOpacity="0.25" fontSize="13" fontFamily="var(--font-mono)" letterSpacing="6">
|
||||
<DEV_ENGINE />
|
||||
</text>
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
{/* ─── 2. Upper-Mid Right: Cisco Network Topology & Graph Nodes (Zooms out subtly on scroll) ─── */}
|
||||
{/* ─── 2. Cisco Network Topology (Upper Right in Landscape / Right Gutter in Portrait) ─── */}
|
||||
<motion.div
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: isPortrait
|
||||
? { y: ptShape2Y, opacity: ptShape2Opacity }
|
||||
: {
|
||||
x: shape2FinalX,
|
||||
y: shape2FinalY,
|
||||
rotate: shape2Rotate,
|
||||
scale: shape2Scale,
|
||||
x: lsShape2FinalX,
|
||||
y: lsShape2FinalY,
|
||||
rotate: lsShape2Rotate,
|
||||
scale: lsShape2Scale,
|
||||
opacity: lsShape2Opacity,
|
||||
}
|
||||
}
|
||||
className="absolute top-[20%] right-[4%] sm:right-[10%] w-[350px] sm:w-[440px] h-[350px] sm:h-[440px] will-change-transform"
|
||||
className={
|
||||
isPortrait
|
||||
? 'absolute top-[30%] -right-12 w-[260px] h-[260px] will-change-transform'
|
||||
: 'absolute top-[8%] right-[3%] sm:right-[7%] w-[310px] lg:w-[390px] h-[310px] lg:h-[390px] will-change-transform'
|
||||
}
|
||||
>
|
||||
<svg viewBox="0 0 500 500" className="w-full h-full drop-shadow-xs">
|
||||
{/* Connecting Bus Lines */}
|
||||
<line x1="250" y1="250" x2="120" y2="130" stroke="var(--secondary-color)" strokeOpacity="0.20" strokeWidth="1.5" />
|
||||
<line x1="250" y1="250" x2="380" y2="120" stroke="var(--secondary-color)" strokeOpacity="0.20" strokeWidth="1.5" />
|
||||
<line x1="250" y1="250" x2="390" y2="380" stroke="var(--secondary-color)" strokeOpacity="0.20" strokeWidth="1.5" />
|
||||
@@ -193,13 +186,10 @@ export default function Background() {
|
||||
<line x1="120" y1="130" x2="380" y2="120" stroke="var(--secondary-color)" strokeOpacity="0.12" strokeWidth="1" strokeDasharray="4 4" />
|
||||
<line x1="110" y1="370" x2="390" y2="380" stroke="var(--secondary-color)" strokeOpacity="0.12" strokeWidth="1" strokeDasharray="4 4" />
|
||||
|
||||
{/* Central Hub Switch / Router Node */}
|
||||
<circle cx="250" cy="250" r="50" fill="var(--secondary-color)" fillOpacity="0.05" stroke="var(--secondary-color)" strokeOpacity="0.28" strokeWidth="1.8" />
|
||||
<circle cx="250" cy="250" r="68" fill="none" stroke="var(--secondary-color)" strokeOpacity="0.12" strokeWidth="1" strokeDasharray="5 5" />
|
||||
{/* Core Arrows Symbol in Center */}
|
||||
<path d="M 235 240 L 250 225 L 265 240 M 250 225 L 250 275 M 235 260 L 250 275 L 265 260" stroke="var(--secondary-color)" strokeOpacity="0.35" strokeWidth="2" strokeLinecap="round" />
|
||||
|
||||
{/* Satellite Nodes */}
|
||||
<circle cx="120" cy="130" r="28" fill="var(--primary-color)" fillOpacity="0.04" stroke="var(--primary-color)" strokeOpacity="0.22" strokeWidth="1.5" />
|
||||
<circle cx="120" cy="130" r="10" fill="var(--primary-color)" fillOpacity="0.25" />
|
||||
|
||||
@@ -212,145 +202,89 @@ export default function Background() {
|
||||
<circle cx="110" cy="370" r="26" fill="var(--secondary-color)" fillOpacity="0.04" stroke="var(--secondary-color)" strokeOpacity="0.22" strokeWidth="1.5" />
|
||||
<circle cx="110" cy="370" r="9" fill="var(--secondary-color)" fillOpacity="0.25" />
|
||||
|
||||
{/* Network Data Tag */}
|
||||
<text
|
||||
x="250"
|
||||
y="335"
|
||||
textAnchor="middle"
|
||||
fill="var(--secondary-color)"
|
||||
fillOpacity="0.25"
|
||||
fontSize="12"
|
||||
fontFamily="var(--font-mono)"
|
||||
letterSpacing="4"
|
||||
>
|
||||
<text x="250" y="335" textAnchor="middle" fill="var(--secondary-color)" fillOpacity="0.25" fontSize="12" fontFamily="var(--font-mono)" letterSpacing="4">
|
||||
NODE_TOPOLOGY
|
||||
</text>
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
{/* ─── 3. Lower-Mid Left: Floating Developer Terminal / IDE Blueprint (Zooms in subtly on scroll) ─── */}
|
||||
{/* ─── 3. Terminal IDE Blueprint (Mid-Left in Landscape / Left Gutter in Portrait) ─── */}
|
||||
<motion.div
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: isPortrait
|
||||
? { y: ptShape3Y, opacity: ptShape3Opacity }
|
||||
: {
|
||||
x: shape3FinalX,
|
||||
y: shape3FinalY,
|
||||
rotate: shape3Rotate,
|
||||
scale: shape3Scale,
|
||||
x: lsShape3FinalX,
|
||||
y: lsShape3FinalY,
|
||||
rotate: lsShape3Rotate,
|
||||
scale: lsShape3Scale,
|
||||
}
|
||||
}
|
||||
className="absolute top-[56%] left-[4%] sm:left-[11%] w-[340px] sm:w-[430px] h-[340px] sm:h-[430px] will-change-transform"
|
||||
className={
|
||||
isPortrait
|
||||
? 'absolute top-[54%] -left-12 w-[260px] h-[260px] will-change-transform'
|
||||
: 'absolute top-[54%] left-[3%] sm:left-[8%] w-[310px] lg:w-[390px] h-[310px] lg:h-[390px] will-change-transform'
|
||||
}
|
||||
>
|
||||
<svg viewBox="0 0 480 380" className="w-full h-full">
|
||||
{/* Main Terminal Window Frame */}
|
||||
<rect
|
||||
x="30"
|
||||
y="30"
|
||||
width="420"
|
||||
height="320"
|
||||
rx="20"
|
||||
fill="var(--primary-color)"
|
||||
fillOpacity="0.03"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.22"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
{/* Terminal Window Header Bar */}
|
||||
<rect x="30" y="30" width="420" height="320" rx="20" fill="var(--primary-color)" fillOpacity="0.03" stroke="var(--primary-color)" strokeOpacity="0.22" strokeWidth="1.5" />
|
||||
<line x1="30" y1="75" x2="450" y2="75" stroke="var(--primary-color)" strokeOpacity="0.18" strokeWidth="1.2" />
|
||||
{/* 3 Header Window Dots */}
|
||||
<circle cx="65" cy="53" r="6" fill="var(--primary-color)" fillOpacity="0.30" />
|
||||
<circle cx="85" cy="53" r="6" fill="var(--primary-color)" fillOpacity="0.20" />
|
||||
<circle cx="105" cy="53" r="6" fill="var(--primary-color)" fillOpacity="0.20" />
|
||||
|
||||
{/* Terminal Header Title */}
|
||||
<text x="240" y="58" textAnchor="middle" fill="var(--primary-color)" fillOpacity="0.25" fontSize="11" fontFamily="var(--font-mono)">
|
||||
portfolio_runtime.sh
|
||||
</text>
|
||||
|
||||
{/* Terminal Prompt >_ */}
|
||||
<text x="65" y="125" fill="var(--primary-color)" fillOpacity="0.40" fontSize="20" fontFamily="var(--font-mono)" fontWeight="bold">
|
||||
>_
|
||||
</text>
|
||||
{/* Mock Code Line Bars */}
|
||||
<rect x="110" y="114" width="180" height="12" rx="4" fill="var(--primary-color)" fillOpacity="0.14" />
|
||||
<rect x="65" y="155" width="260" height="10" rx="4" fill="var(--foreground)" fillOpacity="0.08" />
|
||||
<rect x="65" y="185" width="200" height="10" rx="4" fill="var(--foreground)" fillOpacity="0.08" />
|
||||
<rect x="65" y="215" width="310" height="10" rx="4" fill="var(--secondary-color)" fillOpacity="0.12" />
|
||||
<rect x="65" y="245" width="150" height="10" rx="4" fill="var(--foreground)" fillOpacity="0.08" />
|
||||
<rect x="65" y="275" width="230" height="10" rx="4" fill="var(--primary-color)" fillOpacity="0.10" />
|
||||
|
||||
{/* Blinking Cursor Bar */}
|
||||
<rect x="302" y="112" width="10" height="16" rx="2" fill="var(--primary-color)" fillOpacity="0.35" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
{/* ─── 4. Bottom-Right: Git Commit Graph & Curly Braces { } (Zooms out subtly on scroll) ─── */}
|
||||
{/* ─── 4. Git Commit Tree & Braces (Lower-Right in Landscape / Bottom Crest in Portrait) ─── */}
|
||||
<motion.div
|
||||
style={
|
||||
reducedMotion
|
||||
? {}
|
||||
: isPortrait
|
||||
? { y: ptShape4Y, opacity: ptShape4Opacity }
|
||||
: {
|
||||
x: shape4FinalX,
|
||||
y: shape4FinalY,
|
||||
rotate: shape4Rotate,
|
||||
scale: shape4Scale,
|
||||
x: lsShape4FinalX,
|
||||
y: lsShape4FinalY,
|
||||
rotate: lsShape4Rotate,
|
||||
scale: lsShape4Scale,
|
||||
opacity: lsShape4Opacity,
|
||||
}
|
||||
}
|
||||
className="absolute bottom-[4%] right-[5%] sm:right-[12%] w-[360px] sm:w-[450px] h-[360px] sm:h-[450px] will-change-transform"
|
||||
className={
|
||||
isPortrait
|
||||
? 'absolute top-[78%] left-1/2 -translate-x-1/2 w-[270px] h-[270px] will-change-transform'
|
||||
: 'absolute top-[52%] right-[5%] sm:right-[10%] w-[320px] lg:w-[400px] h-[320px] lg:h-[400px] will-change-transform'
|
||||
}
|
||||
>
|
||||
<svg viewBox="0 0 500 500" className="w-full h-full">
|
||||
{/* Left Large Curly Brace { */}
|
||||
<path
|
||||
d="M 140 80 Q 90 80 90 160 Q 90 230 60 250 Q 90 270 90 340 Q 90 420 140 420"
|
||||
fill="none"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.25"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path d="M 140 80 Q 90 80 90 160 Q 90 230 60 250 Q 90 270 90 340 Q 90 420 140 420" fill="none" stroke="var(--primary-color)" strokeOpacity="0.25" strokeWidth="2.5" strokeLinecap="round" />
|
||||
<path d="M 360 80 Q 410 80 410 160 Q 410 230 440 250 Q 410 270 410 340 Q 410 420 360 420" fill="none" stroke="var(--primary-color)" strokeOpacity="0.25" strokeWidth="2.5" strokeLinecap="round" />
|
||||
|
||||
{/* Right Large Curly Brace } */}
|
||||
<path
|
||||
d="M 360 80 Q 410 80 410 160 Q 410 230 440 250 Q 410 270 410 340 Q 410 420 360 420"
|
||||
fill="none"
|
||||
stroke="var(--primary-color)"
|
||||
strokeOpacity="0.25"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Git Commit Branch Graph in Center */}
|
||||
{/* Main Branch Line */}
|
||||
<line x1="220" y1="100" x2="220" y2="400" stroke="var(--primary-color)" strokeOpacity="0.25" strokeWidth="2" />
|
||||
{/* Feature Branch Curve */}
|
||||
<path
|
||||
d="M 220 160 C 220 200 280 200 280 250 C 280 300 220 300 220 340"
|
||||
fill="none"
|
||||
stroke="var(--secondary-color)"
|
||||
strokeOpacity="0.28"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5 3"
|
||||
/>
|
||||
<path d="M 220 160 C 220 200 280 200 280 250 C 280 300 220 300 220 340" fill="none" stroke="var(--secondary-color)" strokeOpacity="0.28" strokeWidth="2" strokeDasharray="5 3" />
|
||||
|
||||
{/* Commit Nodes */}
|
||||
<circle cx="220" cy="120" r="9" fill="var(--primary-color)" fillOpacity="0.35" stroke="var(--primary-color)" strokeWidth="2" />
|
||||
<circle cx="220" cy="180" r="7" fill="var(--primary-color)" fillOpacity="0.30" />
|
||||
<circle cx="280" cy="250" r="9" fill="var(--secondary-color)" fillOpacity="0.40" stroke="var(--secondary-color)" strokeWidth="2" />
|
||||
<circle cx="220" cy="320" r="7" fill="var(--primary-color)" fillOpacity="0.30" />
|
||||
<circle cx="220" cy="380" r="9" fill="var(--primary-color)" fillOpacity="0.35" stroke="var(--primary-color)" strokeWidth="2" />
|
||||
|
||||
{/* Git Branch Label */}
|
||||
<text
|
||||
x="250"
|
||||
y="450"
|
||||
textAnchor="middle"
|
||||
fill="var(--primary-color)"
|
||||
fillOpacity="0.25"
|
||||
fontSize="12"
|
||||
fontFamily="var(--font-mono)"
|
||||
letterSpacing="5"
|
||||
>
|
||||
<text x="250" y="450" textAnchor="middle" fill="var(--primary-color)" fillOpacity="0.25" fontSize="12" fontFamily="var(--font-mono)" letterSpacing="5">
|
||||
git:main [HEAD]
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
Reference in new issue
Block a user