React-spring: What's best way to window scroll with react-spring?

Created on 14 Feb 2019  Â·  19Comments  Â·  Source: pmndrs/react-spring

@drcmda Would you say setting onFrame window.scroll makes sense? In terms of performance..

question

Most helpful comment

Hey, for posterity and in case anyone runs into the same question, here is a functional code sandbox:
https://codesandbox.io/s/interesting-waterfall-pgo0y

All 19 comments

https://www.react-spring.io/docs/hooks/basics

Scroll to the 5 running demo things, one of them is scrollTop

https://www.react-spring.io/docs/hooks/basics

Scroll to the 5 running demo things, one of them is scrollTop
Hi
It seems not working and also doesn't exist scrollTop prop into animated.div prop types.

screen shot 2019-03-01 at 1 46 21 am

@rzkhosroshahi use onFrame property to set window.scroll...

@rzkhosroshahi use onFrame property to set window.scroll...

I tried but I can't figure out how and where use onFrame...
Could you write basic example in codesandbox or sth, please?

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

Hey, for posterity and in case anyone runs into the same question, here is a functional code sandbox:
https://codesandbox.io/s/interesting-waterfall-pgo0y

Why all of the thumbs down on the previous answer? It's the only example that I was able to create working code from.

Is there a better way?


Thanks dbismut, creating a working example is extremely helpful!

I think it’s because the sandbox link was broken when I ran out of free sandboxes. Now that I’m on a paid plan I’ve been restoring those ones. Feel free to upvote if the code is useful to you 🤗

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

thx @dbismut. I adjusted your code a bit to allow a user to break out of the forced scrolling. Unfortunately the stop method which is returned by useSpring(fn) didn't stop the animation though. That's why there is an isStopped check.

@yannickschuchmann Please try the latest canary if you have time. The stop function should be fixed in there.

Is there a way to do this with the react-spring/renderprops API?

Sorry to bump, but I had problems if the pageYOffset was not 0 (if the page was scrolled somewhere when clicking the link), so had a look at it. Don't expect the stopScroll() to work without _v9_ of _react-spring_, I tried with 9.0.0-canary.808.17.55c5691. This is about how it ended up on my end:

import { useSpring, config } from "react-spring"
/**
 * use like let { scrollTo } = useScrollToElement()
 * let elemRef = useRef()
 * scrollTo(elemRef.current)
 */
export function useScrollToElement() {
  let [, setScrollPos, stopScroll] = useSpring(() => ({
    immediate: false,
    y: window.pageYOffset,
    x: window.pageXOffset,
    onFrame: ({ x, y }) => {
      window.scrollTo(x, y)
    },
    onRest: () => {
      window.removeEventListener("wheel", stopScrollOnWheel)
    },
    config: config.slow,
  }))

  let scrollTo = node => {
    window.addEventListener("wheel", stopScrollOnWheel)
    setScrollPos({
      y:
        node.getBoundingClientRect().top >= 0
          ? node.getBoundingClientRect().top + window.pageYOffset
          : window.pageYOffset + node.getBoundingClientRect().top,
      // Also added support for vertical, shouldn't introduce any problem if your 
      // page isn't wider than 100vw, but could reflect some unexpected behaviour
      // if you do, and want to scroll to element where element.left doesn't 
      // intersect with the viewport's left edge.
      x:
        node.getBoundingClientRect().left >= 0
          ? node.getBoundingClientRect().left + window.pageXOffset
          : window.pageXOffset + node.getBoundingClientRect().top,
    })
  }

  function stopScrollOnWheel() {
    window.removeEventListener("wheel", stopScrollOnWheel)
    stopScroll()
  }

  return { scrollTo }
}

@martinhj @yannickschuchmann - these implementations only run once for me. If I invoke scrollTo/scrollToTarget it works the first time but not on subsequent calls. Am I missing something?

@JakeElder
subsequent calls are working for me. the only difference to the above snippet and my production is that instead of using the useRef I directly grab the dom element directly through document.getElementById('myId').

I can't remember why I changed that. Could be the issue you're describing.

Hope that helps

For anyone like me ended up with finding onframe had disappeared, now it is renamed to onchange

_This is a great animation library but I'd really hope it could have better and up-to-date document_

_This is a great animation library but I'd really hope it could have better and up-to-date document_

Check out: https://aleclarson.github.io/react-spring/v9/
I think it is a bit too early to switch the documentation on the react-spring site when _v9_ still is rolling out release candidates.

@martinhj
Thanks a lot. Yeah I understand. But it would be better if someone could add a note or enter point for v9 doc in the current one. -> "If you are using v9, check the new document here."

@martinhj thanks for your answer, it got me where I need to be. Your expression

y:
        node.getBoundingClientRect().top >= 0
          ? node.getBoundingClientRect().top + window.pageYOffset
          : window.pageYOffset + node.getBoundingClientRect().top,

is a bit weird though, since the if and else branch are basically the same, just in different order (a+b vs b+a). I changed it to

node.getBoundingClientRect().top === 0
        ? 0
        : node.getBoundingClientRect().top +
          window.pageYOffset 

since the only case which was not working with your solution was scrolling to the very first element. Didn't really investigate further, since this now works for me ¯\_(ツ)_/¯

Once again, thanks for your answer!

@martinhj
do we have a comprehensive 9.0.0-rc.3 example for scroll to div? Not seeing anything.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anton-g picture anton-g  Â·  3Comments

mkhoussid picture mkhoussid  Â·  3Comments

cmmartin picture cmmartin  Â·  3Comments

fortezhuo picture fortezhuo  Â·  3Comments

Oba-One picture Oba-One  Â·  3Comments