简体   繁体   中英

How to scroll to an element using react-spring?

I have read through the entire react-spring docs and there doesn't seem to be a clear way to do this.

My attempt:

import React, { useRef, useState } from "react"
import { animated, useSpring } from "react-spring"

const App = () => {
    const scrollDestinationRef = useRef()

    const [elementScroll, setElementScroll] = useState(false)

    const buttonClickHandler = () => setElementScroll(prevState => !prevState)

    const scrollAnimation = useSpring({
        scroll: elementScroll
            ? scrollDestinationRef.current.getBoundingClientRect().top
            : 0
    })

    return (
        <main>
            {/* Click to scroll to destination */}
            <animated.button
                onClick={buttonClickHandler}
                scrollTop={scrollAnimation.scroll}
                style={{
                    height: "1000px",
                    width: "100%",
                    backgroundColor: "tomato"
                }}
            >
                Scroll to destination
            </animated.button>

            {/* Scroll destination */}
            <div
                ref={scrollDestinationRef}
                style={{
                    height: "200px",
                    width: "200px",
                    backgroundColor: "green"
                }}
            ></div>
        </main>
    )
}

export default App

I'm using a ref and hooks for my attempt.

The useRef is attached the scroll destination in-order to find its offset top from the website's ceiling.

I use useState to toggle between the state on click to trigger the scroll.

I use useSpring to trigger an animation that goes from 0 to the scroll destination's scroll top aka getBoundingClientRect().top .

Can anyone assist in solving this?

There doesn't to be much explanation online, thanks!

useSpring returns a function to set/update animated values. You can use that function to assign a new value to your animated variable. Then, you can use the onFrame property to update the scroll position.

Define your spring like this:

const [y, setY] = useSpring(() => ({
    immediate: false,
    y: 0,
    onFrame: props => {
      window.scroll(0, props.y);
    },
    config: config.slow,
  }));

Then use setY function to start the spring, like this:

 <button
        onClick={() => {
          setY({ y: scrollDestinationRef.current.getBoundingClientRect().top });
        }}
      >
        Click to scroll
 </button>

When you click the button it will assign a new value to y variable in your spring, and onFrame function will be called upon every update.

Note that we call window.scroll function from onFrame property in useSpring .

See working demo here .

const targetElement = useRef(null)
const [, setY] = useSpring(() => ({ y: 0 }))

let isStopped = false
const onWheel = () => {
  isStopped = true
  window.removeEventListener('wheel', onWheel)
}

const scrollToTarget = () => {
  const element = targetElement.current
  const value = window.scrollY + element.getBoundingClientRect().top

  window.addEventListener('wheel', onWheel)

  setY({
    y: value,
    reset: true,
    from: { y: window.scrollY },
    onRest: () => {
      isStopped = false
      window.removeEventListener('wheel', onWheel)
    },
    onFrame: props => {
      if (!isStopped) {
        window.scroll(0, props.y)
      }
    }
  })
}

This version also allows the user to break out of the forced scrolling by using wheel event. (I personally hate forced scrolling :D)

useSpring(fn) does return a stop method besides the set . But I couldn't make it work with that. I posted it to a related github issue . If you read this, maybe there's a good answer for that already :)

For now it uses a little workaround with isStopped flag.


UPDATE:

Seems like there is actually something fishy with stop fn which should be addressed in v9 canary . Haven't tested since v9 is not stable yet.

Finally after getting through https://github.com/pmndrs/react-spring/issues/544 , answer from Yannick Schuchmann here worked for me, I just had to change onFrame to onChange

I made a custom hook for the solution: https://github.com/TomasSestak/react-spring-scroll-to-hook

react-sring version: 9.0.0-rc.3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM