fix particles amount

This commit is contained in:
2026-01-12 18:06:42 -06:00
parent efc68a4486
commit b78fea0762
3 changed files with 160 additions and 36 deletions

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useRef } from 'react';
const ParticlesBackground: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const prevWidth = useRef(window.innerWidth);
useEffect(() => {
const canvas = canvasRef.current;
@@ -69,11 +70,16 @@ const ParticlesBackground: React.FC = () => {
const init = () => {
if (!canvas) return;
canvas.width = window.innerWidth;
// Use logical height that works well with the 120vh style
canvas.height = window.innerHeight;
// Reduce particle count on mobile/portrait screens
const isPortrait = canvas.height > canvas.width;
const particleCount = isPortrait ? 90 : 180;
// Calculate particle count based on screen area (resolution)
// Formula: sqrt(width * height) / factor
// Desktop (1920x1080) -> ~120 particles
// Mobile (390x844) -> ~48 particles
// Mobile Landscape (844x390) -> ~48 particles (Same as portrait!)
const area = canvas.width * canvas.height;
const particleCount = Math.floor(Math.sqrt(area) / 12);
particles = [];
for (let i = 0; i < particleCount; i++) {
@@ -134,6 +140,11 @@ const ParticlesBackground: React.FC = () => {
animate();
const handleResize = () => {
// Ignore vertical-only resizes (addressing mobile browser bar toggle issue)
if (window.innerWidth === prevWidth.current) {
return;
}
prevWidth.current = window.innerWidth;
init();
}
@@ -153,7 +164,7 @@ const ParticlesBackground: React.FC = () => {
top: 0,
left: 0,
width: '100%',
height: '100%',
height: '120vh', // Extend well below viewport to cover mobile browser bar retraction
zIndex: -1, // Behind everything
}}
/>

View File

@@ -1,44 +1,67 @@
import { useState, useEffect } from "react";
import { Link, useLocation } from "react-router-dom";
import "../styles/components/header.css";
export default function FloatingHeader() {
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen);
};
// Close menu when route changes
useEffect(() => {
setIsMenuOpen(false);
}, [location]);
return (
<header className="floating-header">
<nav className="header-nav">
<Link
to="/"
className={`nav-link ${location.pathname === "/" ? "active" : ""}`}
<div className="header-content">
<button
className={`mobile-toggle ${isMenuOpen ? "open" : ""}`}
onClick={toggleMenu}
aria-label="Toggle navigation"
>
Home
</Link>
<Link
to="/skills"
className={`nav-link ${location.pathname === "/skills" ? "active" : ""}`}
>
Skills
</Link>
<Link
to="/work-experience"
className={`nav-link ${location.pathname === "/work-experience" ? "active" : ""}`}
>
Work Experience
</Link>
<Link
to="/projects"
className={`nav-link ${location.pathname === "/projects" ? "active" : ""}`}
>
Projects and Sidequests
</Link>
<Link
to="/about"
className={`nav-link ${location.pathname === "/about" ? "active" : ""}`}
>
Personal About Me
</Link>
<span className="hamburger-line"></span>
<span className="hamburger-line"></span>
<span className="hamburger-line"></span>
</button>
</nav>
<nav className={`header-nav ${isMenuOpen ? "is-open" : ""}`}>
<Link
to="/"
className={`nav-link ${location.pathname === "/" ? "active" : ""}`}
>
Home
</Link>
<Link
to="/skills"
className={`nav-link ${location.pathname === "/skills" ? "active" : ""}`}
>
Skills
</Link>
<Link
to="/work-experience"
className={`nav-link ${location.pathname === "/work-experience" ? "active" : ""}`}
>
Work Experience
</Link>
<Link
to="/projects"
className={`nav-link ${location.pathname === "/projects" ? "active" : ""}`}
>
Projects and Sidequests
</Link>
<Link
to="/about"
className={`nav-link ${location.pathname === "/about" ? "active" : ""}`}
>
Personal About Me
</Link>
</nav>
</div>
</header>
);
}

View File

@@ -13,12 +13,13 @@
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
margin: 0;
box-sizing: border-box;
}
.header-nav {
display: flex;
justify-content: center;
gap: clamp(10px, 2vw, 40px);
gap: clamp(8px, 2vw, 32px);
flex-wrap: nowrap;
padding: 0 20px;
}
@@ -61,3 +62,92 @@
.nav-link.active::after {
width: 100%;
}
/* Mobile Navigation Styles */
.header-content {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.mobile-toggle {
display: none;
flex-direction: column;
justify-content: space-between;
width: 30px;
height: 21px;
background: transparent;
border: none;
cursor: pointer;
z-index: 1001;
padding: 0;
}
.hamburger-line {
width: 100%;
height: 3px;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 3px;
transition: all 0.3s ease;
}
@media (max-width: 1024px) {
.floating-header {
padding: 15px 20px;
text-align: left;
}
.header-content {
justify-content: flex-end;
/* Align hamburger to right */
}
.mobile-toggle {
display: flex;
}
/* Animate Hamburger to X */
.mobile-toggle.open .hamburger-line:nth-child(1) {
transform: translateY(9px) rotate(45deg);
}
.mobile-toggle.open .hamburger-line:nth-child(2) {
opacity: 0;
}
.mobile-toggle.open .hamburger-line:nth-child(3) {
transform: translateY(-9px) rotate(-45deg);
}
.header-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh;
background: rgba(10, 10, 30, 0.95);
backdrop-filter: blur(15px);
flex-direction: column;
justify-content: center;
align-items: center;
gap: 30px;
transform: translateX(100%);
transition: transform 0.3s ease-in-out;
z-index: 999;
/* Below toggle button */
padding: 0;
}
.header-nav.is-open {
transform: translateX(0);
}
.nav-link {
font-size: 24px;
padding: 10px 0;
}
}