OB电竞开户
In this article, we’ll take a closer look at how Framer Motion helps us in creating awesome animations. We’ll learn how motion components work and learn how to chain animations together. We’ll look into how to make gesture-triggered, timed, and scroll animations with Framer motion. Along the way, we’ll use the things we learn to build five demo applications I’ve set up to show us how we can integrate Framer Motion into real-world applications.
This tutorial will be beneficial to readers who are interested in integrating animations in their React application.
Note: This article requires a basic understanding of React and CSS.
What Is Framer Motion?
Framer Motion is an animation library that makes creating animations easy. Its simplified API helps us abstract the complexities behind animations and allows us to create animations with ease.
Motion Components
These are the building blocks of Framer motion. Motion components are created by prefixing
motion
to your regular HTML and SVG element (e.g,
motion.h1
). Motion components can accept several props, with the basic one being the
animate
prop. This prop takes in an object where we define the properties of that component we want to animate. The properties we define will be animated when the component mounts in the DOM.
Let’s animate an h1 text using Framer Motion. First, we install the framer-motion library and import
motion
.
npm i framer-motion import { motion } from 'framer-motion';
Then we convert the h1 into a motion component.
This is a motion component
This will cause the
h1
to slide 20px to the right and move 20px up when it loads. When units aren’t added, calculations are done using pixels. However, you can explicitly set the units you want the calculations to be based on,
animate={{x: "20rem", y: "-20rem"}}>
.
By default, a motion component will be animated from the state defined from its styles to those in the
animate
prop. However, if we wanted to, we could hijack and define the initial animation state of the component using the
initial
prop. While the
animate
prop is used to define the behavior of components when they mount, the
initial
prop defines their behavior before they mount.
If we want our h1 to come in from the left, we control that using the initial prop.
This is a motion component
Now, when the
h1
mounts, it slides in from the left.
We are not limited to a single animation. We can define a series of animations called
keyframes
in an array of values. Each value will get animated in sequence.
This is a motion component
The
transition
prop allows us to define how the animations occur. With it, we define how values animate from one state to another. Among other things, we can define the
duration
,
delay
, and
type
of animation using this prop.
This is a motion component
Say we were to animate several motion components simultaneously, like in the code snippet below.
This is a motion h1
This is a motion h2
This is a motion h3
This is a motion h4
While this works, the
variants
prop in Framer Motion enables us to extract our animation definitions into a variants object. Not only do
variants
make our code cleaner, but they allow us to create even more powerful and complex animations.
Extracting our animation definitions into variants objects, we have this:
const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } } const H2Variants = { initial: { y: -1000 }, animate: { y: 0 }, transition: { type: "tween", duration: 1, delay: .4 } } const H3Variants = { initial:{ x: 100, opacity: 0 }, animate:{ x: 0, opacity: 1 } } const H4Variants = { initial:{ scale: 0.7 }, animate:{ scale: 1.7 }, transition:{ type: "tween", duration: "2", delay: "1" } }
Instead of passing the animation definitions into a component’s
initial
and
animate
props directly, we extract these definitions into standalone variant objects. In the variant objects, we define variant names that describe each animation’s name as variants.
This is a motion h1
This is a motion h2
This is a motion h3
This is a motion h4
In the
variants
prop, we pass in the name of the variant objects for each motion component and then pass in the animations to the
initial
and
animate
props.
We can take our current setup with variants further to reduce repetition. Using variants, we can propagate animation attributes down through the DOM from a parent motion component. For this to work, we create variants for the parent
motion.div
with similar animation names in its variant object as its children. By doing this, we won’t have to pass the animation names’ to each child component. Behind the scenes, the parent element handles that for us.
const ContainerVariants = { initial: {}, animate: {} }; const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } }; //more variants below
This is a motion h1
This is a motion h2
This is a motion h3
This is a motion h4
Now we have a cleaner code with no repetitions. We turned the container div to a motion component so we could pass in the
ContainerVariants
object we defined. Since we don’t define any animations on the container, we pass in empty objects to
initial
and
animate
. Your animation names must be the same in every variant object for the propagation to work.
Now we understand the basics of Framer Motion. Let’s begin building our fist of 5 demo applications.
Icon Shop
We can create interactive animations based on gestures. Motion components are currently able to listen for hover, tap, pan, and drag gesture detection. We’ll be building this
Icon Shop
app using the
whileHover
prop.
Components
-
App.js
: this holds the heading texts.
-
Card.jsx
: here, we define the animations for the icon cards. -
CardContainer.jsx
: we import and loop through the icons.
-
styles.js
: create, style, and export the motion components. I used styled-components for styling the components.
Let’s start with
App.js
.
import { H1, H2 } from "./Styles"; import CardContainer from "./CardContainer"; return (
Icon Shop
Hover over the cards to see the motion magic
);
We import the
H1
and
H2
motion components we created in the
Styles.js
file. Since they are motion components, we use the
initial
and
animate
props to define their behavior before and when they mount. Here, we also import and display the
CardContiner
component.
Now, the
CardContainer.js
.
import { Container } from "./Styles"; import Card from "./Card"; import { ReactComponent as AddIcon } from "./assets/add.svg"; import { ReactComponent as AirplaneIcon } from "./assets/airplane.svg"; import { ReactComponent as AlarmIcon } from "./assets/alarm.svg"; //more svg imports below... const icons = [
,
,
, //more icons below ]; const CardContainer = () => { return (
{icons.map((icon) => (
))}
); };
Here, we import the SVGs, the
Container
motion component, and the
Card
component.
Similar to
H1
and
H2
in
App.js
, we define animations of the
Container
using the
initial
and
animate
props. When it loads, it will create a cool effect of sliding in from the left of the browser.
Now,
Card.js
import { CardBox, IconBox } from "./Styles"; const CardVariants = { beforeHover: {}, onHover: { scale: 1.1 } }; const IconVariants = { beforeHover: { opacity: 0, y: -50 }, onHover: { opacity: 1, y: 0, scale: 1.5, transition: { type: "tween" } } }; const Card = ({ icon }) => { console.log(icon); return (
{icon}
); };
Here, we create two variant objects with
beforeHover
and
onHover
animations. In the
CardVariants
object, we don’t want to do anything initially, so
beforeHover
is an empty object.
onHover
we increase the scale of the card box.
In the
IconVariants
object, we define the initial state of the
IconBox
in its
beforeHover
. We set its opacity to 0 and push it upwards by 50px. Then, in
onHover
, we set the opacity back to 1, push it back to its default position, and change the transition type to
tween
. Then we pass in the variants to their respective motion components. We make use of the propagation, so we don’t need to explicitly set the
initial
and
animate
props to the
IconBox
component.
Animated Navbar
We’ll build a simple Navigation component, and we’ll see how we can create timing relationships between parent and children motion components.
Components
-
App.js
: this holds the heading texts.
-
Styles.js
: create, style, and export the motion components. The components are styled using styled-components.
Let’s take a look at the
App.js
file.
import { Header, Nav, Link, SvgBox } from "./Styles"; function App() { const [isOpen, setIsOpen] = useState(false); const iconVariants = { opened: { rotate: 135 }, closed: { rotate: 0 } }; const menuVariants = { opened: { top: 0, transition: { when: "beforeChildren", staggerChildren: 0.5 } }, closed: { top: "-90vh" } }; const linkVariants = { opened: { opacity: 1, y: 50 }, closed: { opacity: 0, y: 0 } };
We create an
isOpen
state that will be used to check if the Navbar is open or not. We create 3 variant objects,
iconVariants
,
menuVariants
, and
linkVariants
where we define the animations for the
SvgBox
,
Nav
, and
Link
motion components respectively. The
iconVariants
is used to rotate the
SvgBox
135deg when it’s hovered over. We don’t need to add “deg” to the value. In the
menuVariants
, we control the top position of the
Nav
like you would using the
position
property in CSS. We toggle the top position of the
Nav
based on the
isOpen
state.
With variants, we can create timing relationships between parent and children motion components. We define the relationship between parent
Nav
and its child,
Link
using the
when
property in the transition object. Here, set it to
beforeChildren
, so the parent component’s animations will finish before the child’s animation begins.
Using the
staggerChildren
property, we set a timing order for each link. Each link will take 0.5 seconds to appear one after the other. This creates a nice visual cue when the
Nav
is opened. In the
linkVariants
we animate the opacity and the vertical position of each link.
setIsOpen(!isOpen)} >
Here, we pass in the variants to their respective components. In the
SvgBox
, we toggle the state of
isOpen
whenever it’s clicked, then conditionally animate it based on the state. Like the
SvgBox
, we conditionally animate the
Nav
and the
Link
s based on
isOpen
’s state.
Animated Modal
We’ll build a modal component and learn about Framer Motion’s
AnimatePresence
, and how it allows us animate elements as they leave the DOM.
Components:
-
App.js
: we set up theshowModal
state here. -
Modal.jsx
: the actual animation work takes place here. -
Styles.js
: create, style, and export the motion components. The components are styled using styled-components.
Let’s look into
App.js
import { ToggleButton, Container } from "./Styles"; import Modal from "./Modal"; function App() { const [showModal, setShowModal] = useState(false); const toggleModal = () => { setShowModal(!showModal); }; return (
Toggle Modal
); }
We create a
showModal
state that will be used to conditionally render the modal. The
toggleModal
function will toggle the state whenever the
ToggleButton
is clicked.
ToggleButton
is a motion component, so we can define animations for it. When it mounts, it slides in from the left. This animation runs for 0.5 seconds. We also pass in the
showModal
state to the
Modal
through props.
Now,
Modal.jsx
import { AnimatePresence } from "framer-motion"; import { ModalBox, ModalContent, Container } from "./Styles";
{showModal && (
Modal content!!!!
)}
We import
AnimatePresence
from
framer-motion
. It allows us to set exit animations for components when they leave DOM. We conditionally render the
Modal
based on the
showModal
state. We define the animations for the
ModalBox
and
ModalContent
through their
initial
and
animate
props. There’s also a new prop here,
exit
. Having
AnimatePresence
as a wrapper allows us to add exit animations to
ModalBox
in the
exit
prop.
Scroll Animation
We’ll use a combination of the
useAnimation
hook and
react-intersection-observer
to create scroll-triggered animations.
Components
-
App.js
: we set up the animations for theBox
component and render it inApp
-
Styles.js
: create, style, and export the motion components. The components are styled using styled-components.
import React, { useEffect } from "react"; import { useAnimation } from "framer-motion"; import { useInView } from "react-intersection-observer"; import { Container, H1,StyledBox } from "./Styles"; const BoxVariants = { visible: { opacity: 1, x: 0, transition: { duration: 1 } }, hidden: { opacity: 0, x: 300 }, }; const Box = () => { const controls = useAnimation(); const [ref, inView] = useInView(); useEffect(() => { if (inView) { controls.start("visible"); } }, [controls, inView]); return (
); };
The
useAnimation
hook allows us to control the sequences in which our animations occur. We have access to
controls.start
and
controls.stop
methods that we can use to manually start and stop our animations. We pass in the initial
hidden
animaton to
StyledBox
. We pass in the controls we defined with the
start
method to
StyledBox
animate prop.
react-intersection-observer
’s
useInView
hook allows us to track when a component is visible in the viewport. The
useInView
hook gives us access to
ref
, which we pass to the component we want to watch, and the
inView
boolean, that tells us if that element is
inView
or not. We use the
useEffect
to call
controls.start
whenever the element we’re watching,
StyledBox
is in view. We pass in
controls
and
inView
as
useEffect
’s dependencies. Also, we pass in the variants we defined,
BoxVariants
to
StyledBox
.
Hero Animation
We’ll build a cool hero banner animation using the
useCycle
hook. We’ll understand how
useCycle
allows us to cycle through animations.
import React, { useEffect } from "react"; import { useCycle } from "framer-motion"; import { Container, H1, HeroSection, Banner, TextBox } from "./Styles"; import { ReactComponent as BannerIllustration } from "./bighead.svg"; const H1Variants = { initial: { y: -200, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 1 } }, }; const TextVariants = { initial: { x: 400 }, animate: { x: 0, transition: { duration: 0.5 } }, }; const BannerVariants = { animationOne: { x: -250, opacity: 1, transition: { duration: 0.5 } }, animationTwo: { y: [0, -20], opacity: 1, transition: { yoyo: Infinity, ease: "easeIn" }, }, };
We define 3 variants,
H1Variants
,
TextVariants
, and
BannerVariants
. However, our focus is
BannerVariants
. We define 2 animations,
animationOne
and
animationTwo
in
BannerVariants
. These are the animations we pass into the
useCycle
to cycle through.
const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo"); useEffect(() => { setTimeout(() => { cycleAnimation(); }, 2000); }, []);
useCycle
works similar to the
useState
hook. In the destructured array,
animation
represents the animation that is active, whether
animationOne
or
animationTwo
. The
cylceAnimation
function that cycles between the animation we defined. We pass in the animations we want to cycle through into
useCycle
and call
cylceAnimation
after 2 seconds in
useEffect
.
Cool Hero Section Anmiation
Storage shed, troughs feed bale manure, is garden wheat oats at augers. Bulls at rose garden cucumbers mice sunflower wheat in pig. Chainsaw foal hay hook, herbs at combine harvester, children is mallet. Goat goose hen horse. Pick up truck livestock, pets and storage shed, troughs feed bale manure, is garden wheat oats at augers. Lamb.
At the end of everything, we pass in the variants to their respective components and watch the magic happen. With this, the
Banner
will initially slide in from the right based on the animations we defined in
animationOne
, and after 2 seconds,
cycleAnimation
will be called which will trigger
animationTwo
.
As a wise Pig once said, “that’s all folks.”
Conclusion
We’ve gone through the basics of Framer Motion and seen some demo projects that give us a glimpse of the range of animations we can create. However, you can do so much more with it. I encourage you to dive into the docs and go wild.
Resources
- Framer Motion Api Docs , Framer Motion
- react-intersection-observer , npm
- Framer Motion for React, NetNinja