Animation with Framer Motion
Author : JaNakh Pon , January 16, 2022
Tags
Introduction
FramerMotion is a production-ready motion library for React from Framer. It brings declarative animations, effortless layout transitions and gestures while maintaining HTML and SVG semantics.
Motion Elements
To animate HTML and SVG elements, we can use motion component.
There's a motion component for every HTML and SVG element, for instance motion.div, motion.circle etc.
<motion.div className="flex justify-center m-5">
<h2>Inside Motion Component</h2>
</motion.div>
Animation
We can control motion component's animations using its properties. For example,
<motion.div
initial={{}}
animate={{}}
transition={{}}
...
className="flex justify-center m-5"
>
<h2>Inside Motion Component</h2>
</motion.div>
Transition
A Transition is an object which can contain Orchestration props like delay, that schedule the animation as a whole.
We can define the types of animation such as Tween, Spring or Inertia
<motion.div
animate={{ rotate: 180 }}
transition={{ type: 'spring' }}
...
className="flex justify-center m-5"
>
<h2>Inside Motion Component</h2>
</motion.div>
Transition Orchestration
We can use delay, delayChildren, staggerChildren, staggerDirection and "beforeChildren" | "afterChildren" to orchestrate animation.
staggerChildren defines how much time should pass between each child’s animation starting. when defines the point at which these animations should begin. A value of afterChildren means that the stagger delay will occur after the child animation. beforeChildren means the opposite — waiting the defined time before the animation.The major difference here is on the first animation, whether it starts immediately (afterChildren) or waits briefly (beforeChildren). We don’t want to delay the first child’s animation, so we’ll use afterChildren.
<motion.div
initial={{
y: "-100",
opacity: 0,
}}
animate={{
y: 0,
opacity: 1,
transition: {
duration: 0.5,
type: "spring",
stiffness: 60,
when: "beforeChildren",
},
}}
...
>
<motion.ul ... >
{/* put individual animation to li */}
{data.map((d,i) => <motion.li key={i}></motion.li>)}
</motion.ul>
</motion.div>
LayoutGroup
Group motion components that should perform layout animations together.
By default, motion components with a layout prop will attempt to detect and animate layout changes every time they commit a React render.
It might be the case that components in different trees affect each other's layout.
Reorder
Create drag-to-reorder effects with a simple set of components.
The Reorder components can be used to create drag-to-reorder lists, like reorderable tabs or todo items.
AnimatePresence
Animate components when they're removed from the React tree.
AnimatePresence allows components to animate out when they're removed from the React tree.
To put it simply, AnimatePresence is a special component that detects whenever a component is removed from the DOM. So, we can add animation to our components right before it disappear!
Implementation #1
In implementation part 1, we will write a todo app with a textarea
for creating task and list of ul/li
for displaying tasks.
textarea
component will be slided down from top to bottom
a bit with bouncing effect
and items of the list
will be slided in from left to right one after one
on load and each item will be faded out from left to right
on delete.
Firstly, we will use LayoutGroup to group our animated components and use AnimatePresence to wrap around the li
elements so we can add exit
animation onDelete:
<LayoutGroup>
<div className="max-w-3xl mx-auto my-6">
<motion.form
initial={{
y: "-100",
opacity: 0,
}}
animate={{
y: 0,
opacity: 1,
transition: {
duration: 1,
delay: 0.2,
type: "spring",
bounce: 0.85,
},
}}
whileTap={{
scale: 0.95,
}}
whileHover={{
y: -5,
transition: {
type: "spring",
bounce: 0.85,
},
}}
className="w-full"
>
<textarea
className="
form-control
block
w-full
px-2
py-1.5
text-base
font-normal
text-gray-700
bg-white bg-clip-padding
border border-solid border-gray-300
rounded
m-0
focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
id="exampleFormControlTextarea1"
rows="4"
placeholder="Your new task"
value={newTask.description}
onChange={handleChange}
onKeyDown={handleEnterPress}
></textarea>
</motion.form>
</div>
<motion.ul
layout
className="overflow-y-auto"
style={{ maxHeight: "80vh" }}
>
<AnimatePresence>
{tasks.map((task, i) => {
return (
<motion.li
layout
initial={{ x: -100, opacity: 0 }}
animate={{
x: 0,
opacity: 0.8,
transition: {
type: "spring",
stiffness: 150,
duration: 1,
delay: i / 4,
},
}}
exit={{
x: 50,
opacity: 0,
transition: {
ease: "easeInOut",
duration: 0.25,
delay: 0.15,
},
}}
whileHover={{
opacity: 1,
scale: 1.03,
transition: {
type: "spring",
bounce: 0.85,
},
}}
key={task.id}
className="max-w-3xl p-4 mx-auto my-4 rounded-lg shadow-lg bg-slate-200"
>
<h4 className="text-lg font-semibold text-gray-800">
{task.description}
</h4>
<p className="mt-2 text-sm font-semibold text-gray-600">
Created on {task.date}
</p>
<div className="flex justify-end space-x-2">
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-6 h-6 cursor-pointer"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
onClick={() => removeTask(task.id)}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</div>
</motion.li>
);
})}
</AnimatePresence>
</motion.ul>
</LayoutGroup>
Implementation #2
In this part, we will use Reorder component in place of ul/li - elements, so, we can reorder our task list:
<Reorder.Group
...
axis="y" values={tasks} onReorder={setTasks}>
<AnimatePresence>
{tasks.map((task, i) => {
return (
<Reorder.Item
...
key={task.id}
value={task}
className="max-w-3xl p-4 mx-auto my-4 rounded-lg shadow-lg bg-slate-200">
...
</Reorder.Item>
);
})}
</AnimatePresence>
</Reorder.Group>
Ref => Framer Motion tutorial: How to easily create React animations
Ref => Framer Motion - beautiful animations and interactions for React