scroller-motion

๐Ÿ›น Elegant motion scrolling for React

Usage no npm install needed!

<script type="module">
  import scrollerMotion from 'https://cdn.skypack.dev/scroller-motion';
</script>

README

๐Ÿ›น
scroller-motion

NPM Package Version Project Storybook Tests Workflow Status Formatting Workflow Status

Bringing motion scrolling to React, built upon framer-motion


  1. Installation
  2. Usage
  3. Props
  4. useScrollerMotion
  5. Listeners
  6. Recipes
  7. About
  8. Contributing
  9. License

Installation

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

Usage

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;
}

Props

All props are optional.

disabled

type: boolean
default: false

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).

scale

type: number
default: 1

View demo

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.

spring

type: SpringOptions
default: { mass: 1.25, stiffness: 200, damping: 50 }

View demo

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} />.

useScrollerMotion hook

View demo

This hook allows you to consume the internal MotionValue values, returning an object of the following type:

{ scrollX: MotionValue, scrollY: MotionValue, x: MotionValue, y: MotionValue }
  • scrollX & scrollY: The current (spring) scroll position.
  • x & y: The current transform (useful for calculating scroll position when scale is in-use).

It must be used within a <ScrollerMotion />, to read the values in the parent component see Motion Listeners.

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>
)

Motion Listeners

View demo

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 React, { 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 the native scroll value (without any spring motion) we suggest using framer-motion's useViewportScroll.

Recipes

  • Scroll to hash: Custom hook for handling scrolling to an element via a #hash anchor.

About

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 2.2kb gzipped

It's still considered a beta release, with a public roadmap for the stable v1 available here.

Note: Currently scroller-motion only supports motion scrolling on the main window/body. The stable release should allow for motion scrolling on any DOM element.

Contributing

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

License

Released under the MIT License.
Authored and maintained by Wombak with help from contributors.

wombak.xyz ยท GitHub @wombak ยท Twitter @wombak_xyz