Jump to content
Search Community

Animate Tabs

Poora
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Posted

Hi,

How would one go about creating animated tabs like this with GSAP.

https://buildui.com/recipes/animated-tabs

The above link has been created in framer motion. And I believe framer motions Animate Presence is similar to GSAP FLIP. I've tried doing it with FLIP but haven't had much success.

 

Would love some help pointing me in the right direction. I'm building this in NextJS. Below is my code. The pill element is added/removed behind the text whenever the path changes.

 

export default function NavTextZoop({ text, href, className }) {
	const path = usePathname();
	const state = useRef(null);

	useGSAP(() => {
		state.current = Flip.getState(".navWrapper, .pill");
		console.log(state.current)
	});

	useEffect(() => {
		Flip.from(state.current, { duration: 2 });
	}, [path]);

	return (
		<Link href={href}>
			<div
				className={twMerge(
					"h-[1.5em] relative px-2.5 py-1 flex justify-center items-center navWrapper",
					className
				)}
			>
				{href === path && (
					<div
						className="pill bg-white absolute inset-0 h-full rounded-full"
					></div>
				)}

				<span className="flex h-[1.5em] overflow-hidden items-center justify-center">
					<span className="letter inline-block relative leading-[1.5em] h-[1.5em]">
						{text}
					</span>
				</span>
			</div>
		</Link>
	);
}

 

  • Solution
Posted

Hi,

 

The demo Jack created in this thread should provide a solid starting point:

 

Also check the demo that uses Flip with reparenting, although check the Flip docs as well since this could be achieved with Flip's fit() method as well:

https://gsap.com/docs/v3/Plugins/Flip/static.fit()

 

Hopefully this helps.

Happy Tweening!

Posted

Thank you. @Rodrigo

That was very helpful. I tweaked the solution to be triggered by the path change from the usePathname() hook in NextJS instead of clicking on the links. Here's the code for anyone looking for that solve.

 

const links = [
	{ href: "/", label: "Portfolio" },
	{ href: "/about", label: "About" },
	{ href: "/resume", label: "Resume" },
	{ href: "/contact", label: "Contact" },
];

gsap.registerPlugin(useGSAP, Flip);

export default function NavTextZoop({ className }) {
	const path = usePathname();
	const [currentPath, setCurrentPath] = useState("/");
	const flipState = useRef();
	const childRef = useRef();
	const container = useRef();

	useGSAP(
		() => {
			flipState.current = Flip.getState(childRef.current);
			setCurrentPath(path);
		},
		{ scope: container, dependencies: [path] }
	);

	useGSAP(
		() => {
			if (!flipState.current || !childRef.current) return;
			Flip.from(flipState.current, {
				duration: 0.75,
				ease: sling,
				targets: [childRef.current],
			});
		},
		{ scope: container, dependencies: [currentPath] }
	);

	return (
		<span
			ref={container}
			className={cn("md:flex gap-1.5 navWrapper", className)}
		>
			{links.map((link, i) => (
				<Link key={i} href={link.href}>
					<div className="h-[1.5em] relative px-2.5 py-1 flex justify-center items-center">
						{link.href === currentPath && (
							<div
								ref={childRef}
								data-flip-id="pillId"
								className="pill bg-white absolute inset-0 h-full rounded-full"
							></div>
						)}
						<span className="flex h-[1.5em] overflow-hidden items-center justify-center">
							<span className="letter inline-block relative leading-[1.5em] h-[1.5em] after:h-[1.5em] after:absolute after:left-0 after:top-full after:content-[attr(data-letter)] z-10 mix-blend-exclusion">
								{link.label}
							</span>
						</span>
					</div>
				</Link>
			))}
		</span>
	);
}

 

  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...