Beautiful Animated Nav Bar with React & Framer Motion
Summary
TLDRIn this video, the creator explores the interactive and animated elements of cal.com's landing page, focusing on recreating a navigation bar with a dynamic 'pill' that follows the cursor. Using React and Framer Motion, the tutorial guides viewers through building the animated pill effect, utilizing Tailwind CSS for styling. The process involves setting up state for animating width and position, using refs to determine element dimensions and offsets, and handling hover states for a seamless user experience.
Takeaways
- 🔗 The video discusses creating a navigation bar with an animated pill effect using React and Framer Motion.
- 🎨 The pill effect follows the cursor and changes the text color based on its position, using a 'mixed blend mode' of 'difference'.
- 💻 The tutorial starts with a basic layout using Tailwind CSS, emphasizing that other styling methods can be used.
- 📐 The navigation bar is structured with an unordered list, positioned relatively to allow absolute positioning of the pill.
- 🖌 The pill is styled with a 'pointer' cursor, padding, and text transformations to match the design requirements.
- 🌐 The use of 'z-index' ensures the pill sits behind the text for the desired visual effect.
- 📊 The 'getBoundingClientRect' method is used to dynamically set the width of the pill based on the hovered element.
- 🔄 Framer Motion's 'motion' component is utilized to animate the pill's width, opacity, and position.
- 🔄 The 'useState' hook is employed to manage the pill's animation states, such as width, opacity, and left offset.
- 🔍 The tutorial suggests using 'useRef' to get the dimensions and position of the hovered tab for accurate animation.
- 🔙 An 'onMouseLeave' event is implemented to reset the pill's opacity, ensuring it disappears when the cursor leaves the navigation bar.
Q & A
What is the main focus of the video?
-The main focus of the video is to demonstrate how to recreate a cool animated navigation bar with a following cursor effect using React and Framer Motion.
What is the 'animated pill' mentioned in the video?
-The 'animated pill' refers to a small, pill-shaped element that follows the cursor around when hovering over different sections of the navigation bar.
Why is the 'tactile almost paper-like feel' of Cal.com highlighted in the video?
-The 'tactile almost paper-like feel' is highlighted because the video creator appreciates the unique and engaging brand experience that Cal.com has built into their design, and wants to emulate similar interactions.
What role does Tailwind CSS play in the video?
-Tailwind CSS is used for styling the layout in the video. It's noted that even if viewers are not using Tailwind CSS, they can apply the same styling concepts using other methods.
What is the purpose of setting the position of the unordered list to 'relative'?
-Setting the position of the unordered list to 'relative' is necessary because it allows for the absolute positioning of the cursor element relative to the list, which is required for the animation effect.
How does the 'mixed blend mode' with the value 'difference' affect the text color?
-The 'mixed blend mode' with the value 'difference' is used to create an effect where the text color changes based on the background color behind it. If the text is white on a white background, it turns black, and vice versa for a black background.
What is the significance of the 'cursor' component in the video?
-The 'cursor' component is significant because it represents the animated pill that moves and changes width based on the user's cursor position over the navigation tabs.
How is the width of the animated pill determined in the video?
-The width of the animated pill is determined dynamically based on the width of the tab element that the cursor is hovering over.
What is the purpose of using Framer Motion in the video?
-Framer Motion is used to animate the position, width, and opacity of the cursor element smoothly as the user interacts with the navigation bar.
How does the video handle the cursor animation when the mouse is not hovering over any tab?
-When the mouse is not hovering over any tab, the video handles the cursor animation by setting the opacity back to zero using an 'onMouseLeave' event, effectively hiding the cursor.
What additional insights does the video offer for extending the functionality?
-The video suggests that one could extend the functionality by adding scale, different colors, rotation, or turning it into a full navigation with links.
Outlines
🛠️ Building a Dynamic Navigation Bar
The speaker begins by expressing admiration for the cal.com landing page's tactile, paper-like feel and its cool interactions and animations. They decide to recreate the animated pill effect found in the navigation bar using React and Framer Motion. The process starts with setting up a basic layout using Tailwind CSS, creating a centered component with a background color. A main slide tabs component is then built, consisting of an unordered list of tabs with relative positioning to allow for absolute positioning of a cursor element. The tabs are styled with rounded borders, a white background, and padding. The text is set to uppercase and changes size based on the screen size. The most interesting part is the text color, which uses a mixed blend mode to change colors based on the background. A cursor component is then created, styled to move freely and sit behind the tabs, initially set with a black background. The speaker then introduces the idea of animating the cursor width based on the hovered tab.
🎨 Animating the Cursor with Framer Motion
The speaker continues by setting up state to animate the cursor's opacity, width, and left position based on the hovered tab. They explain how to use Framer Motion's motion component to animate these properties. The animate prop is used to smoothly transition between different styles. The speaker then demonstrates how to use React's useRef hook to get the dimensions and position of the hovered tab. They show how to get the width of the tab using the getBoundingClientRect method and how to use the offsetLeft property to determine the left position. The speaker sets up an onMouseEnter event to update the state with these values, animating the cursor width and opacity. They also ensure that the cursor follows the mouse across different tabs without any distortion.
🔚 Cleanup and Further Exploration
The speaker concludes by addressing the issue of the cursor remaining visible after the mouse leaves the tabs. They add an onMouseLeave event to reset the opacity to zero, making the cursor disappear when not hovering over any tab. They suggest various ways to extend the functionality, such as adding scale, color changes, or rotation, and encourage viewers to explore these possibilities. They also recommend a video by Sam Sakov for an alternate approach using Framer Motion's layout animations. The speaker provides a link to the code for this tutorial on their website, available in both JavaScript and TypeScript, and invites viewers to check out other components as well. They end the video by asking for likes and subscriptions and sign off with a friendly farewell.
Mindmap
Keywords
💡cal.com
💡Landing Page
💡React
💡Framer Motion
💡Tailwind CSS
💡Navigation Bar
💡Animated Pill
💡Cursor
💡Refs
💡Animation
Highlights
The speaker praises cal.com for its tactile, paper-like feel in their brand.
The speaker plans to break down the cool interactions and animations on cal.com's landing page.
A navigation bar with an animated pill that follows the cursor is discussed.
React and Framer Motion are used to recreate the animated pill effect.
The use of Tailwind CSS for styling is mentioned, with assurance that styling will be covered.
A wrapping component is used to center content on the screen with a background color.
An unordered list is used to render tabs with relative positioning for an absolutely positioned cursor.
The tabs have a black border, white background, and padding for styling.
Text is set to uppercase and padding and text size adjust on medium screens.
A mixed blend mode is used to change text color based on the background.
A cursor component is introduced to create the animated pill background.
The cursor has an absolute position and a z-index lower than the tabs.
The cursor's width is animated based on the hovered tab.
State is used to animate the opacity, width, and left position of the cursor.
Framer Motion's motion component is used to animate the list item.
The animate prop in Framer Motion handles the animation between styles.
Refs are used to get the width and left offset of the hovered element.
The getBoundingClientRect function is used to obtain the size of the hovered element.
The left value of the cursor is set based on the offset left of the hovered element.
An onMouseLeave event is added to hide the cursor when the mouse is not hovering.
The speaker suggests extending the project by adding scale, color, or rotation.
An alternate method using Framer Motion for layout animations is recommended.
The code for the project will be available on the speaker's website in JavaScript and TypeScript.
Transcripts
if you've watched many of my videos
you'll have heard me use cal.com for a
number of different examples for a cool
landing page a Cool brand and a lot of
cool interactions and animations I just
love this kind of like tactile almost
paper-like feel that they've been able
to build into their brand and for a
while now I've been wanting to start
breaking down some of the cool
interactions and animations they built
onto their landing page I figured a good
start would be something like this
navigation bar up here at the top it has
this cool animated pill that actually
follows your cursor around whenever you
Mouse over different sections and we can
recreate this effect Rel ly easily using
react and framer motion enough talking
about it let's go ahead and jump into
some code taking a look at my code I've
already got some basic layout Happening
Here with styling using Tailwind CSS if
you're not using Tailwind CSS not a big
deal feel free to use whatever you want
we'll make sure that we actually go over
all of the styling that's going on here
so I've got a wrapping component right
here this could just well be your screen
like your page and for this I'm just
taking everything putting it in the
middle of the screen and giving it some
background color so nothing crazy going
on here for our main slide tabs
component right here I just have an
unordered list which is rendering a
number of list items that's these tabs
right here obviously for your example
you could use anchor tags or whatever it
may be but for this example I'm just
going to use a basic unordered list this
unordered list has a position of
relative we need this because here in a
second we're going to absolutely
position a little cursor that's going to
be like that little black pill
background and that is going to need to
know that it needs to be relative to
this unordered list I've added a margin
X of Auto again this is really just for
this example to put everything in the
middle of the screen display a flex just
so that all of these tabs sit next to
each other rounded full just to round
out the borders a two pixel black border
background of white and a padding of
four pixels for each of my individual
tabs it's a little bit more interesting
stuff going on so position of relative
with a zindex of 10 so that the text
sits on top of that little pill that's
going to be in the background here in a
second display a block a cursor of
pointer since this isn't an anchor tag
or something some padding on the x-axis
of 12 pixels and on the Y AIS of six
pixels text of extra small to start and
then on medium screen so above 768
pixels I'm just bumping those values up
so on the x axis my padding is 20 pixels
y AIS it is 12 pixels and then also the
text goes up to 16 pixels I'm turning
all of the text to uppercase no matter
what's passed in and then the most
interesting part of what's Happening
Here is these two Styles right here so
you'll notice that I'm setting the text
color to white but the text still looks
black which is kind of weird right well
the reason that that's happening is
because we have this mixed blend mode
set to difference and essentially what
this is going to do is it's going to
take the difference of the text color
with whatever is behind it so in this
case it's just the white background of
the Navar itself and it's going to kind
of make that the color of the text so if
you have white text sitting on top of a
white background it's going to turn it
black but what's cool is it's actually
going to do the opposite of that as well
so if our little background pill thing
is black it's going to turn the text
white and that's going to look really
cool because if we have our cursor thing
in the background and it's overlapping
any of our text just the part that's
actually overlapping is going to change
colors that might sound a little bit bit
confusing but let's go ahead and jump to
the cursor part and we'll see that kind
of come together so down here below my
tab I'm just going to make one more new
component called cursor and for now this
can just return a list item like this
cursor is going to be rendered directly
under our tabs like this and I'm just
going to drop the Styles in for this as
well and we can go ahead and explain
them so I've added a position of
absolute because this needs to be able
to move freely about the background of
our navigation bar a zindex of zero
because we need this to sit behind our
tabs which have a z index of 10 on
smaller screens I've given this a height
of 28 pixels which this is just mapping
directly to what I know the height to be
for our list items you could also base
the height for these based on the top
and bottom absolute positions that's
just not how I happened to do it for
this example we'll notice also that on
medium screens I've bumped that up a
little bit then finally I've just given
this a rounded full to make it all a
rounded Edge as well as a background of
black now we're going to notice if I
save this that we don't actually see
anything yet that's just because we
don't have any height set up yet or
width rather so we can fix that let's
just say width is equal to I don't know
say 36 and there we go now we can kind
of see what I was talking about here so
you'll notice that any of the text that
is overlapping the black pill background
turns white but anything that is not
overlapping stays black so if we have a
situation like this where the pill is
just halfway overlapping some text only
the half that is actually overlapping
turns the opposite color now I don't
actually want to control the width based
on a straight value like this I want to
animate it based on whichever one of
these pills we're currently hovering
over so to see how that can start to
come together I'm going to remove our
width right here and we'll scroll back
up up to our slide tabs and I'm going to
create a piece of state which I am going
to call position so remembering to
import U State that's going to look
something like this I have three
different values that I actually want to
animate at any given point so I want to
animate the opacity I want this to not
be visible whenever we're not hovering
over something I want the width to be
dynamic based on whichever one of these
items we're hovering over and I want
left so this left of zero here to be
based also on the left side of whichever
one of these tabs we're hovering over we
can set these to some random value to
start so you can kind of see what's
going on on so we'll say left of 60
maybe width of 150 and then an opacity
of one and go ahead and pass these into
our cursor like this now coming down to
my cursor component can open this up and
before I actually pass in these Styles I
want to turn this into a motion list
item so that we can animate this value
later so if you're not familiar with
this just remember to install frame or
motion and from frame or motion you can
import the motion component so that will
look something like this what motion is
going to allow us to do is prepend any
of our just normal jsx elements so in
this case this list item right here and
turn it into a motion. li what this
unlocks for us is a whole bunch of
different things but for our specific
example what this mainly does for us is
gives us access to a prop called animate
and this animate prop can just be passed
in any set of styles and it will handle
automatically animating between those
different sets of styles for you so
notice that this position value that
we're passing in already directly maps
to three different CSS Styles so all
that we need to do is take our position
and pass it into animate like this if we
save that now we should see this
starting to come together so we have our
pill in the background it's slightly
overlapping home so that turns white
it's fully overlapping pricing so that
turns white and now we can just change
these values as we hover over different
ones of our tabs and it will
automatically handle animating between
them for us to do this let's start by
zeroing our values back out so we'll say
left of zero width of zero and opacity
of zero and then we'll take our set
position function and pass that into
each of of our tabs so that will look
something like this just pass set
position in and then down on our tab
component we can destructure that and
start animating this value now remember
from our position that there are two
specific values that we need we need
both the width of the actual element
that we are currently hovering over as
well as the offset of the left side of
the element based on the relative
position of the parent so that's kind of
confusing but for example if we were
hovering over home right here the left
offset is essentially going to be zero
right plus maybe a little bit of padding
so we're going to need that offset
between the left side of this container
and the tab if we were hovering over
pricing it's going to be whatever you
know 100 pixels or something like that
from right about here over to the left
side etc etc as you go down the line to
get that value I am going to use refs so
what we're going to need to do to get
that value is store a ref of the list
item and then whenever we Mouse over our
list item we can pull those values
directly off of that ref so just using a
normal react use ref I'll say ref is
equal to use ref
import that from react and we can
default this to null directly on our
list item I will say ref is equal to ref
like this and the event that we want
this to fire on is on Mouse enter like
this so just create an inline function
like this and that should be fine if
you're using typescript to make
typescript happy you're going to need to
do something like this so I'll say if
not ref. current ref. current actually
stores the current value of the
reference then we're going to want to
return and now we can start getting some
of our values first off let's actually
see how we can get our width of the
element that's currently being hovered
over now on any reference to an HTML
element we're going to have a function
called get bounding client wck and
that's going to give us a whole bunch of
different numbers for the size of our
element so to see what I mean let's say
const data is equal to ref.
current. getet bounding client wrecked
and sorry my dogs are barking to see
what this actually gives us let's just
add a console.log and log out our data
I'll hover over one of my pills here
let's say pricing and open this up and
we'll see all the different fun stuff
that this gives us so it gives us things
like the height and the width of our
element this is specifically what we
need but it also gives us the position
of the element on the screen
unfortunately this is not the position
relative to the edge of the container
which is what we need we're going to
have a different way that we're actually
going to get that value so since all
that we need is actually width from this
piece of data I'm going to remove data
and just destructure our value width and
down here we can start animating that
value so I'll say set position and we
can pass in our width now we we also
know that we're going to want to
increase the opacity on Hover so we can
set our opacity to one and this is now
going to cover two of our three Styles
both our width and our opacity but we
still need to figure out how to get our
left value let's start by just setting
our left to zero so we can see that the
width part works if I now hover over
home we'll see that that kind of looks
about right pricing it increases in size
and this little pill is going to keep
expanding to the size of whatever we're
hovering over but to get our left value
we're going to need to do something a
little bit different so over here on
left I'm going to remove my zero and
there's a value directly on our ref
called offset left so it's going to look
something like ref. current. offset left
now you definitely need to make sure
that this outer wrapping UL right here
is relative because offset left is just
going to look at whatever the closest
relative parent is and that's how you're
going to get your value so setting this
to left now we should see this starting
to come together hovering over home and
then pricing and then features it will
now follow me around no matter where I
hover so if I go even off of my element
go all the way over to blog and there's
no Distortion or anything weird going on
whenever you hover over all of these
different elements finally the last
thing that we really need though here is
we'll notice that whenever we remove our
Mouse the element just stays there which
probably isn't what we want so just to
round this off I'm going to come back up
to my unordered list and add one more
call to on Mouse leave this is going to
call a function like this and really all
that I care about is setting the opacity
back to zero so I'll say set position
this can take whatever the previous
value is and just spread that value in
only overriding the opacity setting that
back to zero with all of this done now
hovering over all of my elements
continues to work but as soon as I move
my mouse off of the element it goes away
if I move it back over here it'll
animate from there no matter where I
kind of go and add and remove my mouse
it just keeps following me around now
there's a bunch of different ways that
you could extend this you could add
scale to this you could add different
colors you could make it rotate you
could get all kinds of interesting just
based on adding different cool styles to
this but I will leave that up to you to
play with as well as turning this into a
full navigation if you wanted to add
things like links I will also say that
if you're interested in an alternate way
of doing this also with frame or motion
using layout animations check out this
awesome video by Sam sakov he gets
essentially the same effect that I have
here but with a significantly different
strategy for how he does it so if you're
just learning frame motion it could be
really cool to check out how he did it
and compare that with how I've done it
here the code for all of this will be
free on my website both in JavaScript as
well as in typescript just head to the
link in the description and then hit
code right here if you want JavaScript
or typescript you can toggle between the
two of those as well as a bunch of other
cool components if you want to check
those out anyways that's going to be it
for me if you learned anything I would
massively appreciate a like and a
subscribe until next time peace
浏览更多相关视频
I Built JavaScript-Powered Minimap Scroll Animation
Website PreLoader Animation | Page Reveal Animation GSAP | HTML, CSS, JavaScript x Greensock
Animate nav on scroll - CSS-only & easy to customize
Words in the middle of the horizontal lines (CSS Mastery # 007) | Coder Champ
The Classic ScrollTrigger Animation That Always Impresses
Responsive HTML Table With Pure CSS - Web Design/UI Design
5.0 / 5 (0 votes)