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.