fix particles amount
This commit is contained in:
@@ -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
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user