У меня проблема с анимацией панели навигации. Я использую React и Framer Motion. Мой результат почти готов, но его еще нужно немного доработать, чтобы сделать его идеальным до пикселя. У меня проблемы с анимацией ссылок; они должны быть заблокированы слева, но мои задвигаются справа. Они также должны раскрываться слева направо, один за другим. Не могли бы вы проверить мой код и предложить некоторые улучшения для достижения желаемого результата? Вот короткое видео о том, как это должно анимироваться: https://www.loom.com/share/d882818c384e4af18abeabf92abb2980
import Link from "next/link";
import React, { useState } from "react";
import HamburgerIcon from "./icons/HamburgerIcon";
import { motion } from "framer-motion";
const MenuDesktop = () => {
const [isHovered, setIsHovered] = useState(false);
const navVariants = {
initial: { width: "5rem", opacity: 0, zIndex: 0 },
hover: { width: "100%", opacity: 1, zIndex: 1 },
};
const linkVariants = {
initial: { opacity: 0 },
hover: { opacity: 1 },
};
const iconVariants = {
initial: { opacity: 1 },
hover: { opacity: 0 },
};
return (
<div className = "flex p-0.5 h-20 rounded-15px bg-cc-invert justify-center items-stretch overflow-hidden">
<div className = "relative">
<motion.nav
className = "flex relative h-full overflow-auto"
initial = "initial"
animate = {isHovered ? "hover" : "initial"}
variants = {navVariants}
transition = {{
width: { duration: 0.75, ease: "easeInOut" },
opacity: { duration: 1, ease: "easeInOut" },
}}
onHoverStart = {() => setIsHovered(true)}
onHoverEnd = {() => setIsHovered(false)}
>
<motion.div
className = "font-dmMono tracking-wider text-cc-dark-brown flex items-center gap-2 text-xs uppercase pl-4 bg-cc-invert "
initial = "initial"
animate = {isHovered ? "hover" : "initial"}
variants = {linkVariants}
transition = {{ duration: 0.5 }}
>
<Link className = "px-2 whitespace-nowrap" href = "/development">
Development
</Link>
<Link className = "px-2 whitespace-nowrap" href = "/energo">
Energo
</Link>
<Link className = "px-2 whitespace-nowrap" href = "/company">
Company
</Link>
<Link className = "px-2 whitespace-nowrap" href = "/news">
News
</Link>
<Link className = "px-2 whitespace-nowrap" href = "/career">
Career
</Link>
</motion.div>
</motion.nav>
<motion.div
className = "absolute top-7 right-7 flex items-center cursor-pointer z-0"
initial = "initial"
animate = {isHovered ? "hover" : "initial"}
variants = {iconVariants}
transition = {{ duration: 0.5 }}
>
<HamburgerIcon className = "h-5 cursor-pointer" />
</motion.div>
</div>
<Link
href = "/"
className = "font-dmMono tracking-wider text-cc-dark-brown flex items-center justify-center p-4 bg-cc-light-brown rounded-15px"
>
<span className = "uppercase text-xs px-2">Contact</span>
</Link>
</div>
);
};
export default MenuDesktop;





Нужную анимацию можно получить с помощью свойства staggerChildren в Framer Motion. Это свойство позволяет задерживать анимацию прямых дочерних элементов.
import Link from "next/link";
import React, {
useState
} from "react";
import HamburgerIcon from "./icons/HamburgerIcon";
import {
motion
} from "framer-motion";
const MenuDesktop = () => {
const [isHovered, setIsHovered] = useState(false);
const navVariants = {
initial: {
width: "5rem",
opacity: 0,
zIndex: 0
},
hover: {
width: "100%",
opacity: 1,
zIndex: 1
},
};
const linkContainerVariants = {
initial: {
opacity: 0
},
hover: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2
}
},
};
const linkVariants = {
initial: {
x: -50,
opacity: 0
},
hover: {
x: 0,
opacity: 1
},
};
const iconVariants = {
initial: {
opacity: 1
},
hover: {
opacity: 0
},
};
return ( <
div className = "flex p-0.5 h-20 rounded-15px bg-cc-invert justify-center items-stretch overflow-hidden" >
<
div className = "relative" >
<
motion.nav className = "flex relative h-full overflow-auto"
initial = "initial"
animate = {
isHovered ? "hover" : "initial"
}
variants = {
navVariants
}
transition = {
{
width: {
duration: 0.75,
ease: "easeInOut"
},
opacity: {
duration: 1,
ease: "easeInOut"
},
}
}
onHoverStart = {
() => setIsHovered(true)
}
onHoverEnd = {
() => setIsHovered(false)
} >
<
motion.div className = "font-dmMono tracking-wider text-cc-dark-brown flex items-center gap-2 text-xs uppercase pl-4 bg-cc-invert "
initial = "initial"
animate = {
isHovered ? "hover" : "initial"
}
variants = {
linkContainerVariants
} >
{
["Development", "Energo", "Company", "News", "Career"].map((link) => ( <
motion.div variants = {
linkVariants
}
transition = {
{
duration: 0.5
}
} >
<
Link className = "px-2 whitespace-nowrap"
href = {
`/${link.toLowerCase()}`
} > {
link
} <
/Link> <
/motion.div>
))
} <
/motion.div> <
/motion.nav> <
motion.div className = "absolute top-7 right-7 flex items-center cursor-pointer z-0"
initial = "initial"
animate = {
isHovered ? "hover" : "initial"
}
variants = {
iconVariants
}
transition = {
{
duration: 0.5
}
} >
<
HamburgerIcon className = "h-5 cursor-pointer" / >
<
/motion.div> <
/div> <
Link href = "/"
className = "font-dmMono tracking-wider text-cc-dark-brown flex items-center justify-center p-4 bg-cc-light-brown rounded-15px" >
<
span className = "uppercase text-xs px-2" > Contact < /span> <
/Link> <
/div>
);
};
export default MenuDesktop;