Scroll animations
How to create scroll-linked and scroll-triggered animations in Framer Motion.
There are two predominant types of scroll animations, both of which can be achieved with Framer Motion.
Scroll-linked animations are when the progress of an animation is directly tied to scroll progress. Scroll-triggered animations are when a normal animation is triggered when an element enters or leaves the viewport.
#Scroll-linked animations
Scroll-linked animations are created using motion values and the useScroll
hook.
import { motion, useScroll } from "framer-motion"
function Component() { const { scrollYProgress } = useScroll(); return ( <motion.div style={{ scaleX: scrollYProgress }} /> )}
Read the full useScroll docs to discover how to track element scroll, element positions within the viewport, create parallax effects and more.
#Scroll-triggered animations
Scroll-triggered animations are normal animations that start when an element enters or leaves the viewport.
The whileInView
prop can be used to create scroll-triggered animations by defining a set of properties and, optionally, a transition, to animate to when the element is in view.
<motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }}/>
Note: There's currently a bug in Chrome where IntersectionObserver
doesn't work correctly with SVG elements.
#whileInView: VariantLabels | TargetAndTransition
Properties or variant label to animate to while the element is in view.
#viewport: ViewportOptions
An object of viewport options that define how the viewport is detected.
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }}/>
#onViewportEnter(entry): void
Callback that triggers when the element enters the viewport. Provides the IntersectionObserverEntry
with details of the intersection event, or null
if the browser doesn't support IntersectionObserver
.
#onViewportLeave(entry): void
Callback that triggers when the element leaves the viewport. Provides the IntersectionObserverEntry
with details of the intersection event. Will never be called if the browser doesn't support IntersectionObserver
.
#Viewport options
#once: boolean
If true
, once the element has entered the viewport it will remain in the whileInView
state. No further viewport callbacks will be triggered.
<motion.div initial="hidden" whileInView="visible" viewport={{ once: true }}/>
#root: RefObject<Element>
By default, the element will be considered within the viewport when it enters the window viewport.
Pass a ref
to both an ancestor element and to viewport.root
to use that ancestor element as the measured viewport instead.
function Component() { const scrollRef = useRef(null) return ( <div ref={scrollRef} style={{ overflow: "scroll" }}> <motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ root: scrollRef }} /> </div> )}
#margin: string
A margin to add to the viewport when detecting whether the element has entered it.
Defaults to "0px"
. A single value can be used to add a margin on every side, e.g. "200px"
. Or, multiple values can be defined to assign a margin to each axis in the order of top/right/bottom/left, e.g. "0px -20px 0px 100px"
.
#amount: "some" | "all" | number
Defaults to "some"
, this option defines the amount of the element that has to intersect with the viewport in order for it to be considered within view.
#fallback: boolean
Framer Motion uses IntersectionObserver
to detect viewport enter/exit. If IntersectionObserver
is not available in the current browser, onViewportEnter
will be fired and whileInView
set when the component first mounts.
By setting fallback: false
this behaviour will be disabled. No viewport events will be fired and whileInView
will never be set.