With the Framer Motion library, we can render animations in our React app easily.
In this article, we’ll take a look at how to get started with Framer Motion.
Scale Correction
Layout animations are performed with the transform
property so we see smooth animations.
Animations may distort child elements.
We can fix this with the layout
prop.
For example, we can write:
App.js
import React, { useState } from "react";
import { motion } from "framer-motion";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
layout
data-isOpen={isOpen}
initial={{ borderRadius: 50 }}
className="parent"
onClick={() => setIsOpen(!isOpen)}
>
<motion.div layout className="child" />
</motion.div>
);
}
styles.css
html,
body {
min-height: 100vh;
padding: 0;
margin: 0;
}
* {
box-sizing: border-box;
}
body {
background: green;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
}
.App {
font-family: sans-serif;
text-align: center;
}
.parent {
background: white;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.parent[data-isOpen="true"] {
width: 200px;
height: 200px;
}
.child {
width: 40px;
height: 40px;
background: green;
border-radius: 50%;
}
We animate the outer div by expanding it when we click on it.
We add the layout
prop to the inner div so that we don’t get distorting when we animate the outer div.
Transforms can also distort boxShadow
and borderRadius
values.
To keep them constant, we can set the in the initial
prop:
App.js
import React, { useState } from "react";
import { motion } from "framer-motion";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
layout
data-isOpen={isOpen}
initial={{ borderRadius: 50 }}
className="parent"
onClick={() => setIsOpen(!isOpen)}
>
<motion.div layout className="child" initial={{ borderRadius: "20%" }} />
</motion.div>
);
}
styles.css
html,
body {
min-height: 100vh;
padding: 0;
margin: 0;
}
* {
box-sizing: border-box;
}
body {
background: green;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
}
.App {
font-family: sans-serif;
text-align: center;
}
.parent {
background: white;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.parent[data-isOpen="true"] {
width: 200px;
height: 200px;
}
.child {
width: 40px;
height: 40px;
background: green;
}
We set the initial
prop of the child div.
And we remove the border-radius
from styles.css
.
Customizing Layout Animations
We can customize layout animations with the transition
property.
For example, we can write:
App.js
import React, { useState } from "react";
import { motion } from "framer-motion";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
layout
data-isOpen={isOpen}
initial={{ borderRadius: 50 }}
className="parent"
onClick={() => setIsOpen(!isOpen)}
transition={{
layoutX: { duration: 0.3 },
layoutY: { delay: 0.2, duration: 0.3 }
}}
>
<motion.div layout className="child" />
</motion.div>
);
}
styles.css
html,
body {
min-height: 100vh;
padding: 0;
margin: 0;
}
* {
box-sizing: border-box;
}
body {
background: green;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
}
.App {
font-family: sans-serif;
text-align: center;
}
.parent {
background: white;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.parent[data-isOpen="true"] {
width: 200px;
height: 200px;
}
.child {
width: 40px;
height: 40px;
background: green;
border-radius: 50%;
}
We set the transition
prop to add a delay when we animate the y
direction.
We also set the duration of the animation in the y
direction to 0.3 seconds.
And we set the animation in the x
direction to 0.3 seconds.
Conclusion
We can correct distortions with layout animations.
And we can control how layout animations are rendered with Framer Motion.