I'm using the vanilla js parallax tutorial from Design Course because I don't plan to use any libraries (lax.js, etc.). This parallax script works great, because it offers the basic vertical and horizontal parallaxes that I need; however, the problem is that when applied to the divs that are below the viewport, it becomes problematic because 1) their initial transform value is affected by the script, 2) they already get the scroll effect before entering the viewport. The further the divs are below the viewport, the worse the problem is. I've been tinkering with getBoundingClientRect() and window.scrollY + window.InnerHeight to get the parallax to trigger only after entering the viewport, but still can't find a way to make it work. Here's my initial code.
UPDATE: I've edited teh JS by also incorporating Intersection Observer. While I can now trigger the parallax inside the viewport, including using rootMargin, but the way the elements move is not yet smooth because apparently, the translate value is already set. I still need some help to make the parallax work smoothly as it should.
HTML
<section id="top-section">
<div
id="blue-box"
class="boxes parallax"
data-rate="0.25"
data-direction="vertical"
></div>
<div
id="green-box"
class="boxes parallax"
data-ratex="0.25"
data-ratey="1"
data-direction="horizontal"
></div>
</section>
<section id="middle-section">
<div
id="red-box"
class="boxes parallax"
data-rate="0.5"
data-direction="vertical"
></div>
</section>
<section id="bottom-section">
<div
id="yellow-box"
class="boxes parallax"
data-rate="1"
data-direction="vertical"
></div>
</section>
CSS
section {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#top-section {
background: black;
}
#middle-section {
background: white;
}
#bottom-section {
background: blue;
}
.boxes {
width: 100px;
height: 100px;
}
#blue-box {
background: blue;
}
#green-box {
background: green;
}
#red-box {
background: red;
}
#yellow-box {
background: yellow;
}
JS
const parallaxEls = document.querySelectorAll('.parallax');
const parallaxObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('paractive', entry.isIntersecting);
});
},
{
rootMargin: '-250px',
}
);
parallaxEls.forEach((parallaxEl) => {
parallaxObserver.observe(parallaxEl);
});
window.addEventListener('scroll', function (e) {
const parallaxed = document.querySelectorAll('.paractive');
let index = 0,
length = parallaxed.length;
for (index; index < length; index++) {
const pos = window.scrollY * parallaxed[index].dataset.rate;
if (parallaxed[index].dataset.direction === 'vertical') {
parallaxed[index].style.transform = 'translate3d(0, ' + pos + 'px, 0)';
} else {
const posX = window.scrollY * parallaxed[index].dataset.ratex;
const posY = window.scrollY * parallaxed[index].dataset.ratey;
parallaxed[index].style.transform =
'translate3d(' + posX + 'px, ' + posY + 'px, 0)';
}
}
});
As you can see, there is no problem with the blue and green divs, but the red and yellow boxes don't look good. Is there some way to modify the JS so that the parallax for lower divs starts to kick in only after their top edge (perhaps getBoundingClientRect().top) reaches a particular coordinate within the viewport (like window.scrollY + window.innerHeight or (window.scrollY + window.innerHeight) - dataset.trigger) with the dataset.trigger value definable in the HTML)?