导航栏组件代码
import Humburger from "./ui/Humburger";
const navlins = ["HOME", "PAGES", "PRODUCTS", "ARTICLES", "CONTACT"];
const Navbar = () => {
const [isNavbarActive, setIsNavbarActive] = useState(false);
const toogleNavbar = () => setIsNavbarActive(!isNavbarActive);
return (
<>
<div className="relative bg-white h-[4.5rem] z-20">
<div className="w-full h-full flex justify-between items-center px-4">
<Image
width={120}
height={70}
src="https://cdn.prod.website-files.com/65478d390c8996a757a4faaa/65c4aacccb9a41d80d0d66f7_Stride-logo-dark.svg"
alt="nav-logo"
/>
<div className="hidden">
<ul className="flex w-full border border-green-500">
<li>Home</li>
<li>Pages</li>
<li>Product</li>
<li>Article</li>
<li>contact</li>
</ul>
</div>
<div className="flex items-center gap-4">
<div className="relative h-10 w-10 p-2">
<Image
className="z-0 inset-0"
src="https://cdn.prod.website-files.com/65478d390c8996a757a4faaa/659e5cb3e032b694713f63c0_Add%20to%20basket.svg"
width={20}
height={20}
alt="cart-icon"
/>
<div className="z-20 absolute shadow-xl top-1 -right-[2px] bg-gray-200 text-black rounded-full text-[0.6em] h-[1.1rem] w-[1.1rem] grid place-content-center font-bold">
5
</div>
</div>
<button className="border flex items-center justify-center border-slate-400 hover:focus-within:none bg-black text-white rounded-full px-4 py-1.5 hover:bg-white transition-all ease-in hover:text-black">
Login
</button>
<Humburger onClick={() => setIsNavbarActive(!isNavbarActive)} />
</div>
</div>
</div>
<AnimatePresence mode="popLayout">
{isNavbarActive && <NavbarDropdown />}
</AnimatePresence>
</>
);
};
export default Navbar;
const NavbarDropdown = () => {
const dropdownVariants = {
initial: {
opacity: 0,
height: 0,
},
enter: {
opacity: 1,
height: "14rem",
transition: {
ease: [0.83, 0, 0.17, 1],
duration: 0.5,
staggerChildren: 0.1,
staggerDirection: 1,
delayChildren: 0.2,
// Stagger the li children after the dropdown opens
},
},
exit: {
height: 0,
transition: {
ease: [0.83, 0, 0.17, 1],
staggerChildren: 0.1, // Stagger the li children on exit as well
staggerDirection: -1,
delay: 0.3,
// Reverse the direction (exit from bottom to top)
// Delay before shrinking the dropdown after the li items
},
},
};
const liVariants = {
initial: {
opacity: 0,
x: -50,
},
enter: {
opacity: 1,
x: 0,
transition: {
ease: [0.22, 1, 0.36, 1],
duration: 0.5,
},
},
exit: {
opacity: 0,
x: -50,
transition: {
ease: [0.22, 1, 0.36, 1],
},
},
};
return (
<motion.div
variants={dropdownVariants}
initial="initial"
animate="enter"
exit="exit"
className="absolute origin-top top-[4.6rem] w-full bg-white drop-shadow-lg"
>
<motion.ul className="space-y-5 px-3">
{navlins.map((navlin, index) => (
<motion.li
custom={index}
key={index}
variants={liVariants} // Apply variants to each li item
className="text-gray-600 text-[1em] font-normal flex items-center gap-5"
>
{navlin}
{index === 0 || index === 1 ? (
<IoMdArrowDown size={24} color="gray" />
) : (
<></>
)}
</motion.li>
))}
</motion.ul>
</motion.div>
);
};
汉堡组件代码
"use client";
import { useAnimate } from "framer-motion";
import React, { forwardRef, useState } from "react";
const Humburger = forwardRef<HTMLDivElement, { onClick: () => void }>(
({ onClick }, forwardedRef) => {
const [scope, animate] = useAnimate();
const [isHumburgOpen, setIsHumburgOpen] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
const animateHumburger = async () => {
if (isAnimating) return null;
setIsAnimating(!isAnimating);
onClick();
if (!isHumburgOpen) {
animate(
"#top",
{
y: "0.45rem",
},
{
ease: [0.36, 0, 0.66, -0.56],
duration: 0.5,
}
);
await animate(
"#bottom",
{
y: `-${0.45}rem`,
opacity: 0,
},
{
ease: [0.36, 0, 0.66, -0.56],
duration: 0.5,
}
);
animate(
"#top",
{
rotate: "136deg",
},
{
ease: [0.22, 1, 0.36, 1],
duration: 0.5,
}
);
animate(
"#middle",
{
rotate: "43deg",
},
{
ease: [0.22, 1, 0.36, 1],
duration: 0.5,
}
);
setIsHumburgOpen(!isHumburgOpen);
} else {
animate(
"#top",
{
rotate: "0deg",
},
{
ease: [0.36, 0, 0.66, -0.56],
duration: 0.5,
}
);
await animate(
"#middle",
{
rotate: "0deg",
},
{
ease: [0.36, 0, 0.66, -0.56],
duration: 0.5,
}
);
animate(
"#top",
{
y: "0rem",
},
{
ease: [0.22, 1, 0.36, 1],
duration: 0.5,
}
);
await animate(
"#bottom",
{
y: 0,
opacity: 1,
},
{
ease: [0.22, 1, 0.36, 1],
duration: 0.5,
}
);
setIsHumburgOpen(!isHumburgOpen);
}
setIsAnimating(false);
};
return (
<div ref={scope}>
<a
onClick={animateHumburger}
className="hover:focus-within:outline-none hover:focus:outline-none flex flex-col items-end justify-center h-24 gap-1 mt-50 cursor-pointer"
>
<span
id="top"
className="origin-center h-[3px] w-6 bg-black block rounded-full"
></span>
<span
id="middle"
className="h-[3px] w-6 bg-black rounded-full"
></span>
<span
id="bottom"
className="h-[3px] w-4 bg-black rounded-full"
></span>
</a>
</div>
);
}
);
export default Humburger;
animateHumburger 函数只是将三个垂直条变成一个十字。我正在使用 framermotion 来制作动画,并使用 useAnimate hook,其中我必须将范围传递给主容器并将 id 传递给子元素,通过它们我将在 useanimatehook 中使用它们来制作动画。这个 humburger 组件完成后,我在 navbar 组件中使用它,但出现错误。但是 ref 正在 humburger 组件本身中使用,为什么我需要按照 chatgpt 的建议传递 useRef hook,尽管它也没有解决问题。不能给函数组件提供 refs 这到底是什么意思?即使在 react 文档中他们已经在函数组件中使用过还是我弄错了。请纠正我并帮我解决问题?
另外给我解决问题的方法如何解决这个错误
问题是
NavbarDropdown
没有包装在里面forwardRef
。文档指出:
您正在使用自定义组件(
NavbarDropdown
)作为的直接子组件AnimatePresence
,并且还使用popLayout
模式:因此这个限制也适用于你。
要修复此问题,请用 包裹
NavbarDropdown
并将forwardRef
该引用应用于要“弹出”的元素。我在这里假设它是 中的第一个元素NavbarDropdown
。