Reputation: 97
I am using the Parallax component from react-spring to achieve a parallax effect on a parent component. Within this component, there are a bunch of background shapes that are within the parallax
<Parallax ref={parallaxRef} pages={7.465} scrolling={props.stageData.contractActive && !isMobile ? true : false}>
<div className="desktop-bg-shapes">
<ParallaxLayer offset={0} speed={0.5}>
<span className="rectangle bg-rectangle rectangle-1 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={0} speed={0.5}>
<span className="rectangle bg-rectangle rectangle-2 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={0} speed={0.2}>
<span className="rectangle bg-rectangle rectangle-3 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={1.3} speed={0.6}>
<span className="rectangle bg-rectangle rectangle-4 light-blue"></span>
</ParallaxLayer>
<ParallaxLayer offset={4} speed={0.4}>
<span className="rectangle bg-rectangle rectangle-5 blue"></span>
</ParallaxLayer>
<ParallaxLayer offset={6.7} speed={0.8}>
<span className="rectangle bg-rectangle rectangle-6 light-blue"></span>
</ParallaxLayer>
</div>
And these are moving when I scroll through the main part of the page, which is content that is centred on the page (to the left and right are the above shapes that move when scrolling through this content) and is also within the parallax on the parent component
<ParallaxLayer ref={parallaxMainLayer} offset={0} speed={0}>
<div className="viewport desktop">
CONTENT HERE
....etc
The parallax effect works. But another effect has stopped working. Previously when the user scrolled down the page, the header changed colour based on where the user was scrolling. Now however the onScroll event isn't firing and I am now sure how to get it to fire
Here is the header within the above parent component
<Header readCarefulStart={readCarefulStart} agreeSect={agreeSect}/>
I am passing the above ref to getBoundingClientRect() in the Header component itself. I am using useEffect to register the onScroll in the header component like so
useEffect(() => {
const handleScroll = (event) => {
// fill info
if (props.readCarefulStart.current.getBoundingClientRect().top > 150)
props.activeFillInfo()
// read carefully
else if (props.readCarefulStart.current.getBoundingClientRect().top <= 150 && props.readCarefulStart.current.getBoundingClientRect().top >= -3464 && props.agreeSect.current.getBoundingClientRect().top === 0)
props.activeReadCarefully()
// signature
else if (props.readCarefulStart.current.getBoundingClientRect().top < -3464)
props.activeSignature()
})
}
window.addEventListener('scroll', handleScroll);
return () => document.removeEventListener('scroll', handleScroll);
}, [])
Before the header was changing colour based on onScroll but now this event doesn't register when I console.log(event). Why could this be?
function App(props){
return (
<Header readCarefulStart={readCarefulStart} agreeSect={agreeSect}/>
<Parallax ref={parallaxRef} pages={7.465} scrolling={props.stageData.contractActive && !isMobile ? true : false}>
<div className="desktop-bg-shapes">
<ParallaxLayer offset={0} speed={0.5}>
<span className="rectangle bg-rectangle rectangle-1 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={0} speed={0.5}>
<span className="rectangle bg-rectangle rectangle-2 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={0} speed={0.2}>
<span className="rectangle bg-rectangle rectangle-3 orange"></span>
</ParallaxLayer>
<ParallaxLayer offset={1.3} speed={0.6}>
<span className="rectangle bg-rectangle rectangle-4 light-blue"></span>
</ParallaxLayer>
<ParallaxLayer offset={4} speed={0.4}>
<span className="rectangle bg-rectangle rectangle-5 blue"></span>
</ParallaxLayer>
<ParallaxLayer offset={6.7} speed={0.8}>
<span className="rectangle bg-rectangle rectangle-6 light-blue"></span>
</ParallaxLayer>
</div>
<ParallaxLayer ref={parallaxMainLayer} offset={0} speed={0}>
<div className="viewport desktop">
CONTENT HERE
....etc
</div>
</ParallaxLayer>
</Parallax>
)
}
function Header(props) {
useEffect(() => {
const handleScroll = (event) => {
// fill info
if (props.readCarefulStart.current.getBoundingClientRect().top > 150)
props.activeFillInfo()
// read carefully
else if (props.readCarefulStart.current.getBoundingClientRect().top <= 150 && props.readCarefulStart.current.getBoundingClientRect().top >= -3464 && props.agreeSect.current.getBoundingClientRect().top === 0)
props.activeReadCarefully()
// signature
else if (props.readCarefulStart.current.getBoundingClientRect().top < -3464)
props.activeSignature()
})
}
window.addEventListener('scroll', handleScroll);
return () => document.removeEventListener('scroll', handleScroll);
}, [])
return(
<header>
<div className="viewport">
<div className="inner">
<nav>
<ul>
<li className='start-header'><span className="num-circle">1</span><span className="text">Start</span></li>
<li className='fill-info'><span className="num-circle">2</span><span className="text">Fill in information</span></li>
<li className='read-carefully'><span className="num-circle">3</span><span className="text">Read Carefully</span></li>
<li className='sign'><span className="num-circle">4</span><span className="text">Sign</span></li>
<li className='agreement'><span className="num-circle">5</span><span className="text">New agreement</span></li>
</ul>
</nav>
</div>
</div>
</header>
)
}
*edit added all code in one section at the end
Upvotes: 0
Views: 3168
Reputation: 754
According to its react-spring documentation,
The Parallax component creates a scrollable container in which ParallaxLayers can be placed or React.Fragments whose only direct children are ParallaxLayers. Because Parallax is a scrollable container all scroll events are fired from the container itself therefore, listening for scroll on window won't work. However, if you want to attach additional events you can use ref.current.container
and after I follow its steps, here's what I get
const ref = useRef<IParallax>();
const scrollListener = () => {
const handleWheelEvent = () => {
const {container, current} = ref.current;
const scrollpercent = current / (container.current.scrollHeight - window.innerHeight);
};
window.addEventListener('wheel', handleWheelEvent);
return () => {
window.removeEventListener('wheel', handleWheelEvent);
};
};
useEffect(scrollListener, []);
return (
<Parallax className={styles['parallax-pane']} pages={5} ref={ref}>
...
Upvotes: 2
Reputation: 21
This worked for me here:
const parallaxRef = useRef<IParallax>(null)
function scrollListener() {
const container = parallaxRef.current?.container
.current as HTMLDivElement
container.onscroll = () => {
setScrollTop(parallaxRef.current?.current as number)
}
return () => {
container.onscroll = null
}
}
useEffect(scrollListener, [])
Marvin's solution unfortunately does not work on mobile devices.
Upvotes: 1
Reputation: 43
A more neat solution would be to set the listener directly from the ref of the Parallax component, rather then using querySelector.
Example:
const Test = () => {
const parallaxRef = useRef()
const onScroll = () =>
console.log(parallaxRef.current.current / parallaxRef.current.space)
useEffect(() => {
if (!parallaxRef.current || !parallaxRef.current.container) return
parallaxRef.current.container.onscroll = onScroll
})
return (
<div>
<Parallax pages={3} ref={parallaxRef}>
<ParallaxLayer offset={0} speed={0.5}>
<span onClick={() => parallaxRef.current.scrollTo(1)}>
Layers can contain anything
</span>
</ParallaxLayer>
...
</Parallax>
</div>
)
}
As you can see from the source of react-spring Parallax, the ref contains references to the two div elements created by the component.
Upvotes: 3
Reputation: 97
I fixed this myself
So the issue was that I was adding an event listener to the window as opposed to the parallax
window.addEventListener('scroll', handleScroll);
So I added a class to the main parallax component. There are two div's created by the parallax at the top level so you need to target two levels above (the grandparent) the immediate child to the parallax in your code
useEffect(() => {
document.querySelector('div.classNameOfChildOfParallax').parentElement.classList.add('parallax-child')
document.querySelector('div.parallax-child').parentElement.classList.add('parallax-main')
}, [])
Then in Header you do this
document.querySelector('div.parallax-main').addEventListener('scroll', handleScroll);
Upvotes: -2