I am building a react project. it has a sticky navigation on scrolling down. I have managed to get a smooth transition when the sticky navigation appears, but when scrolling back to the top it just pops back in without any transition. demo here: https://techhype.netlify.app/
I want the sticky nav to transition back into the top in a smooth natural way. how can i achieve this? here is my nav code:
navigation.jsx:
import React, { useContext, useState, useEffect } from "react";
import { Link, useLocation } from "react-router-dom";
import { content } from "constants/content";
import { UilUserCircle, UilShoppingBag } from "@iconscout/react-unicons";
import Logo from "logo/logo.png";
import LogoNoText from "logo/logo-no-text.png";
import LanguageSelector from "components/LanguageSelector";
import { LangContext } from "utils/LangContext";
import { TabletAndDesktop } from "components/ScreenViewSize";
import "@animated-burgers/burger-rotate/dist/styles.css";
import Tooltip from "@mui/material/Tooltip";
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function Navigation() {
const [lang] = useContext(LangContext);
const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);
const [isClosing, setIsClosing] = useState(false);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const [isScrolled, setIsScrolled] = useState(false);
const location = useLocation();
const toggleMobileMenu = () => {
setMobileMenuOpen(!isMobileMenuOpen);
};
const closeMenu = () => {
setIsClosing(true);
setTimeout(() => {
setMobileMenuOpen(false);
setIsClosing(false);
}, 600); // Adjust the delay to match the slide-up animation duration
};
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
useEffect(() => {
const handleScroll = debounce(() => {
const scrollPosition = window.scrollY;
const scrollThreshold = 12; // Adjust this value as needed
if (scrollPosition < 1) {
window.location.hash = "";
}
setIsScrolled(scrollPosition > scrollThreshold);
}, 20); // Adjust the debounce delay as needed
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return (
<div className={`nav-container${isScrolled ? " sticky " : " "}`}>
<div className="navigation container-inner">
<Link to="/">
<img
src={isScrolled ? LogoNoText : Logo}
className={`logo ${isScrolled ? "logo-hover" : ""}`}
alt="Techhype Logo"
/>
</Link>
<TabletAndDesktop>
<LanguageSelector />
</TabletAndDesktop>
<div
className={`mobile-menu ${isMobileMenuOpen ? "open" : ""} ${
isClosing ? "close" : ""
}`}
>
<LanguageSelector />
<div className="close-button" onClick={closeMenu}>
<span>×</span>
</div>
<nav>
<Link
to="/shop"
className={location.pathname === "/shop" ? "active" : ""}
>
{content[lang]["menuItem1"]}
</Link>
<Link
to="/#howItWorks"
className={
location.pathname === "/" && location.hash === "#howItWorks"
? "active"
: ""
}
onClick={() => {
closeMenu();
}}
>
{content[lang]["menuItem2"]}
</Link>
<Link
to="/#reviews"
className={
location.pathname === "/" && location.hash === "#reviews"
? "active"
: ""
}
onClick={() => {
closeMenu();
}}
>
{content[lang]["menuItem3"]}
</Link>
<Link
to="/contact"
className={location.pathname === "/contact" ? "active" : ""}
>
{content[lang]["menuItem4"]}
</Link>
</nav>
</div>
<nav className="desktop-menu">
<Link
to="/shop"
className={location.pathname === "/shop" ? "active" : ""}
>
{content[lang]["menuItem1"]}
</Link>
<Link
to="/#howItWorks"
className={
location.pathname === "/" && location.hash === "#howItWorks"
? "active"
: ""
}
>
{content[lang]["menuItem2"]}
</Link>
<Link
to="/#reviews"
className={
location.pathname === "/" && location.hash === "#reviews"
? "active"
: ""
}
>
{content[lang]["menuItem3"]}
</Link>
<Link
to="/contact"
className={location.pathname === "/contact" ? "active" : ""}
>
{content[lang]["menuItem4"]}
</Link>
</nav>
<div className="nav-icons">
<Tooltip title="Login">
<Link to="/login">
{windowWidth <= 900 ? (
<UilUserCircle size={28} color="white" />
) : (
<UilUserCircle size={38} color="white" />
)}
</Link>
</Tooltip>
<Tooltip title="Cart">
<Link to="/cart">
{windowWidth <= 900 ? (
<UilShoppingBag size={28} color="white" />
) : (
<UilShoppingBag size={38} color="white" />
)}
</Link>
</Tooltip>
</div>
<div className="burger-menu" onClick={toggleMobileMenu}>
<div className="burger burger-rotate">
<div className="burger-lines"></div>
</div>
</div>
</div>
</div>
);
}
export default Navigation;
Navigation.scss:
.black-background-container {
background-color: $color-darker;
}
.nav-container {
width: 100%;
background-color: $color-darker;
transition: all 0.4s ease-in-out;
a {
display: flex;
align-items: center;
}
svg {
height: 27px;
width: 27px;
}
}
.nav-container.sticky {
position: sticky;
top: 0;
z-index: 100;
background-color: $color-dark;
box-shadow: 0 -6px 10px 5px rgba(0, 0, 0, 0.5);
transition: all 0.4s ease 0s;
@media (min-width: 900px) {
animation: fadeDownDesktop 0.6s ease forwards;
}
@media (max-width: 899px) {
animation: fadeDownMobile 0.6s ease forwards;
.navigation {
padding: 0.6em 1.5em !important;
}
.nav-icons {
gap: 2em;
svg {
width: 22px;
height: 22px;
}
}
}
.navigation {
padding: 1em;
}
.burger {
font-size: 8px !important;
}
.logo {
width: 30px;
transition: transform 0.6s ease-in-out;
transform-origin: center;
&:hover {
transform: rotate(360deg);
filter: brightness(1);
}
}
@media (min-width: 900px) {
.language-selector {
opacity: 0;
z-index: -10;
}
}
}
@keyframes fadeDownDesktop {
from {
top: 0;
opacity: 0;
height: 123px;
background-color: $color-darker;
transform: translateY(-100%);
}
to {
top: 0;
opacity: 1;
height: 62px;
background-color: $color-dark;
transform: translateY(0);
}
}
@keyframes fadeDownMobile {
from {
top: 0;
opacity: 0;
height: 76px;
background-color: $color-darker;
transform: translateY(-100%);
}
to {
top: 0;
opacity: 1;
height: 49.19px;
background-color: $color-dark;
transform: translateY(0);
}
}
.navigation {
display: flex;
justify-content: space-between;
align-items: center;
padding: 2em 1em 1em 1em;
position: relative;
@media (max-width: 900px) {
padding: 1em 1em 0 1em;
}
.desktop-menu {
display: flex;
gap: 60px;
a {
text-decoration: none;
color: white;
font-size: 1rem;
font-weight: bold;
transition: 0.2s ease-in-out;
overflow: hidden;
position: relative;
display: inline-block;
&.active {
&::after {
width: 100%;
right: auto;
left: 0;
}
}
&::after {
content: "";
position: absolute;
bottom: 0;
right: 0; /* Updated: Set the right position instead of left */
width: 0%;
height: 2px;
background-color: $color-hover;
transition: width 0.6s cubic-bezier(0.2, 0.85, 0.32, 1.2);
}
&:hover {
&::after {
width: 100%;
right: auto; /* Updated: Reset the right position on hover */
left: 0; /* Added: Set the left position on hover */
}
}
}
}
.burger-menu {
display: none;
}
.nav-icons {
display: flex;
gap: 1.5em;
svg:hover {
transition: 0.2s ease-in-out;
fill: $color-hover;
}
}
.mobile-menu {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: $color-dark;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 100;
&.open {
animation: slideDown 0.6s ease-in-out forwards;
display: flex;
}
&.close {
animation: slideUp 0.6s ease-in-out forwards;
}
nav {
display: flex;
flex-direction: column;
gap: 20px;
a {
text-decoration: none;
color: white;
font-size: 1.5rem;
font-weight: bold;
transition: 0.2s ease-in-out;
overflow: hidden;
position: relative;
display: inline-block;
&.active {
&::after {
width: 100%;
right: auto;
left: 0;
}
}
&::after {
content: "";
position: absolute;
bottom: 0;
right: 0; /* Updated: Set the right position instead of left */
width: 0%;
height: 2px;
background-color: $color-hover;
transition: width 0.6s cubic-bezier(0.2, 0.85, 0.32, 1.2);
}
&:hover {
color: $color-hover;
}
}
}
.close-button {
position: absolute;
top: 0.6em;
right: 0.8em;
cursor: pointer;
color: white;
font-size: 38px;
font-weight: bold;
&:hover {
color: $color-hover;
}
}
}
@media (max-width: 900px) {
.desktop-menu {
display: none;
}
.burger-menu {
display: flex;
justify-content: flex-end;
align-items: center;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
.burger {
font-size: 10px;
}
&:hover .burger-lines,
&:hover .burger-lines:after,
&:hover .burger-lines:before {
background-color: $color-hover;
}
}
}
}
.logo {
width: 75px;
transition: filter 0.3s ease-in-out;
&:hover {
filter: brightness(0.9);
}
@media only screen and (max-width: 900px) {
width: 60px;
}
@media only screen and (max-width: 600px) {
width: 60px;
}
}
.language-selector {
display: flex;
flex-direction: column;
position: absolute;
right: 0;
top: 0.5em;
label {
font-weight: bold;
color: white;
&:focus {
color: $color-primary;
}
}
@media only screen and (max-width: 900px) {
left: 1.5em;
top: 2em;
right: auto;
}
}
//Animations for the mobile menu
@keyframes slideDown {
from {
top: -100vh;
}
to {
top: 0;
}
}
@keyframes slideUp {
from {
top: 0;
}
to {
top: -100vh;
}
}