Bringing motion scrolling to React, built upon framer-motion
To begin you'll want to install scroller-motion as well as the peer dependencies:
npm install scroller-motion framer-motion react
# or
yarn add scroller-motion framer-motion react
Note: Due to the use of hooks the minimum required version of React is 16.8
Implementing scroller-motion couldn't be easier, simply wrap your page with the <ScrollerMotion>
component. For example in a Next.js app this might look like the following:
/* pages/index.jsx */
import { ScrollerMotion } from 'scroller-motion'
export default () => (
<ScrollerMotion>
<MyComponent />
</ScrollerMotion>
)
Most modern browsers implement an inertia bounce effect to the window while scrolling (upon reaching the start/end). This can cause unwanted visual effects, such as flickering, when using scroller-motion.
To fix this, you can disable overscroll-behavior
in your project with the following CSS:
/* style.css */
html,
body {
overscroll-behavior: none;
}
All props are optional.
Type | boolean |
Default | false |
Description | Completly disables and unmounts the ScrollerMotion component. Any children will be rendered through a React <Fragment> in this case (thus falling back to native scrolling). |
Type | number |
Default | 1 |
Demo | View demo |
Description | Extends the scrollable length of the page, giving a "slow scroll" experience. For example if the page content is 1400px in height, <ScrollerMotion scale={1.5} /> would result in a scrollable length of 2100px (height * scale ).The lowest this value can be is 1 , anything lower will be disregarded and 1 will be used in its place. |
Type | SpringOptions |
Default | { mass: 1.25, stiffness: 200, damping: 50 } |
Demo | View demo |
Description | The main configuration object for the scroll's spring transform, basically the 2nd parameter to framer-motion's useSpring. You can disable the spring scroll by passing a falsy value to this prop, for example: <ScollerMotion spring={null} /> . |
This hook allows you to consume the internal MotionValue
values, returning an object of the following type:
{
scrollX: MotionValue,
scrollXProgress: MotionValue,
scrollY: MotionValue,
scrollYProgress: MotionValue,
x: MotionValue,
y: MotionValue
}
scrollX
&scrollY
: The current scroll position.scrollXProgress
&scrollYProgress
: A0
to1
transform ofscrollX|scrollY
, similar to those returned byuseScroll
.x
&y
: A negative representation ofscrollX|scrollY
.
It must be used within a <ScrollerMotion />
, to read the values in the parent component see Motion Listeners.
ℹ️ For accessing native scroll values (without spring motion or scale calculation) we suggest using framer-motion's
useScroll
.
import { ScrollerMotion, useScrollerMotion } from 'scroller-motion'
import { motion } from 'framer-motion'
const MyComponent = () => {
const { x, y } = useScrollerMotion()
return <motion.div style={{ x, y }}>Hello world</motion.div>
}
export default () => (
<ScrollerMotion>
<MyComponent />
</ScrollerMotion>
)
Another approach if you need to read/use the internal MotionValue
values is via the ref
prop on <ScrollerMotion />
. The type of the ref is the same as the object returned from useScrollerMotion
.
For example, if we want to use the y-axis scroll position:
import { useEffect, useRef } from 'react'
import { useMotionValue } from 'framer-motion'
import { ScrollerMotion, ScrollerMotionRef } from 'scroller-motion'
export default () => {
const scrollerMotion = useRef<ScrollerMotionRef>()
const scrollY = useMotionValue(0)
useEffect(() => {
const unsubscribe = scrollerMotion.current.scrollY.onChange((v) =>
scrollY.set(v)
)
return () => unsubscribe()
}, [scrollY])
return (
<ScrollerMotion ref={scrollerMotion}>
<MyComponent scrollPosition={scrollY} />
</ScrollerMotion>
)
}
ℹ️ For accessing native scroll values (without spring motion or scale calculation) we suggest using framer-motion's
useScroll
.
- Hash anchor scroll: Scroll the window to a DOM element when clicking a
#hash
anchor. - Horizontal scroll: Convert vertical mousewheel events to horizontal window scroll.
- Tab scroll: Scroll the window to a DOM element when pressing the Tab key (useful for a11y purposes).
Scroller-motion was born from the need for motion/smooth scrolling in a couple of React projects. Given the fact that we were already using the beloved framer-motion
for the rest of the animations & transitions, we decided to try it out for page scrolling too - and the results were impressive! Time for an emoji list:
- 🏀 Configurable motion via the
spring
prop - 🖱 "Slow scroll" via the
scale
prop - 📡 Subscribe to the scroll values with
useScrollerMotion
- ⚙️ SSR compatible
- 🤖 Fully typed w/ TypeScript
- 🪝 Built around React hooks
- ⚖️ Only ~2kb gzipped
These are the current scripts available for development:
# Start Storybook
npm run start
# Build dist files
npm run build
# Build static Storybook
npm run build:storybook
# Apply Prettier formatting
npm run prettier:format
# Check Prettier formatting
npm run prettier:check
# Run tests
npm run test
Released under the MIT License.
Authored and maintained by Brad Adams with help from contributors.
breadadams.com · GitHub @breadadams · Twitter @breadadams