I have 3 images that have a parallax animated effect. The problem is that the parallax effect has some delay*
*as @Roko C. Buljan pointed out, correctly.
How to fix?
Here's my code - the effect is only visible under 670px (as can be seen without putting on the full screen mode, here.)
document.addEventListener("DOMContentLoaded", function() {
const parallaxContainer = document.querySelector(".parallax-container");
const windowWidth = window.innerWidth;
let isScrolling = false; // Flag to track scrolling activity
let isScrollingStopped = false; // Flag to track when scrolling has stopped
// Define the Intersection Observer options
const observerOptions = {
root: null,
rootMargin: "0px",
threshold: 1 // Trigger when the entire image is not in the viewport
};
// Create the Intersection Observer
const observer = new IntersectionObserver(function(entries) {
entries.forEach((entry) => {
const image = entry.target;
if (windowWidth < 670) {
if (entry.isIntersecting) {
// Image is in viewport, remove grayscale filter
image.style.filter = "grayscale(0)";
// Pause the parallax animation by removing the animation CSS property
image.style.animation = "none";
} else {
// Image is not in viewport, apply grayscale filter
image.style.filter = "grayscale(1)";
// Resume the parallax animation by restoring the animation CSS property
image.style.animation = "";
}
}
});
}, observerOptions);
// Get all the parallax images and observe each one
const parallaxImages = document.querySelectorAll(".parallax-image");
parallaxImages.forEach((item) => {
observer.observe(item);
});
// Function to update the parallax effect
function updateParallax() {
const scrollPosition = parallaxContainer.scrollLeft;
// Apply the parallax effect to each item with a background image
parallaxImages.forEach((item) => {
const coefficient = parseFloat(item.dataset.parallaxCoefficient) || 1.0;
const xOffset = -scrollPosition * coefficient;
item.style.backgroundPositionX = `${xOffset}px`;
});
// Continue updating the parallax effect while scrolling
if (isScrolling && !isScrollingStopped) {
requestAnimationFrame(updateParallax);
}
}
// Function to handle scrolling end
function handleScrollEnd() {
isScrollingStopped = true;
}
// Start the parallax effect when scrolling begins
parallaxContainer.addEventListener("scroll", function() {
isScrollingStopped = false;
// Set the flag to true when scrolling starts
if (!isScrolling) {
isScrolling = true;
// Request the first frame of the parallax animation
requestAnimationFrame(updateParallax);
}
// Clear the flag after a short delay (adjust the delay time as needed)
clearTimeout(parallaxContainer.dataScrollTimer);
parallaxContainer.dataScrollTimer = setTimeout(function() {
isScrolling = false;
handleScrollEnd(); // Call the function to handle scrolling end
}, 100); // Adjust the delay (in milliseconds) to control the duration of parallax after scroll ends
});
});
.what-we-do {
padding: 88px 60px 40px 60px;
}
.what-we-do .upper-div h2 {
margin: 0;
}
.what-we-do h2 {
font-family: "lato";
font-style: normal;
font-weight: 300;
font-size: 40px;
line-height: 48px;
}
.what-we-do-container {
height: 100%;
display: flex;
flex-direction: column;
}
.pics-div {
display: flex;
flex-direction: row;
gap: 8px;
height: 576px;
overflow: hidden;
}
.pics-div p {
font-family: Lato;
font-size: 20px;
font-weight: 400;
line-height: 28px;
letter-spacing: 0em;
text-align: left;
}
.pic1,
.pic2,
.pic3 {
cursor: pointer;
}
.pic1 {
filter: grayscale(1);
background-image: url(https://picsum.photos/200/300);
background-size: cover;
width: 30%;
padding: 32px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
transition-duration: 1s;
}
.pic2 {
filter: grayscale(1);
background-image: url(https://picsum.photos/200/300);
background-size: cover;
width: 30%;
padding: 32px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
transition-duration: 1s;
}
.pic3 {
filter: grayscale(1);
background-image: url(https://picsum.photos/200/300);
background-size: cover;
width: 30%;
padding: 32px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
transition-duration: 1s;
}
.pic-upper-text {
display: flex;
gap: 32px;
align-items: center;
transition-duration: 1s;
position: relative;
}
.pic1:hover {
width: 50%;
filter: grayscale(0);
transition-duration: 1s;
.pic-upper-text {
gap: 150px;
}
}
.pic2:hover {
width: 50%;
filter: grayscale(0);
transition-duration: 1s;
.pic-upper-text {
gap: 150px;
}
}
.pic3:hover {
width: 50%;
filter: grayscale(0);
transition-duration: 1s;
.pic-upper-text {
gap: 150px;
}
}
section {
display: block;
}
.what-we-do-container {
box-sizing: border-box;
height: auto;
}
.what-we-do-container {
height: 100%;
display: flex;
flex-direction: column;
}
.pics-div {
height: 100%;
width: 100%;
overflow-x: scroll;
margin-bottom: 74px;
}
.pics-div {
display: flex;
flex-direction: row;
gap: 8px;
height: 576px;
overflow: hidden;
overflow-x: hidden;
}
.parallax-container {
display: flex;
overflow-x: scroll;
scroll-behavior: auto;
-webkit-overflow-scrolling: auto;
scroll-snap-type: x mandatory;
}
.parallax-image {
scroll-snap-align: center;
flex: auto;
}
.pic1:hover,
.pic2:hover,
.pic3:hover {
width: 50%;
filter: grayscale(0);
transition-duration: 0.5s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="what-we-do">
<div class="what-we-do-container">
<div class="upper-div">
<h2>Main title</h2>
</div>
<div class="pics-div parallax-container">
<div ontouchstart="changeBullet1()" onclick="window.location.href = 'lorem-ipsum';" class="pic1 parallax-image" data-parallax-coefficient="0.2">
<div class="pic-upper-text">
<h3>Title1</h3>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</div>
<p>
Lorem ipsum
</p>
</div>
<div ontouchstart="changeBullet2()" onclick="window.location.href = 'lorem-ipsum';" class="pic2 parallax-image" data-parallax-coefficient="0.2">
<div class="pic-upper-text">
<h3>Title 2</h3>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</div>
<p>
Lorem ipsum sit dolor amet
</p>
</div>
<div ontouchstart="changeBullet3()" onclick="window.location.href = 'lorem-ipsum';" class="pic3 parallax-image" data-parallax-coefficient="0.2">
<div class="pic-upper-text">
<h3>Title 3</h3>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.33398 20H31.6673" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M20 8.33325L31.6667 19.9999L20 31.6666" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</div>
<p>
Lorem ipsum sit dolor amet
</p>
</div>
</div>
<div class="bullet-container">
<div class="bullet-box">
<div id="bullet1" class="bullet"></div>
<div id="bullet2" class="bullet"></div>
<div id="bullet3" class="bullet"></div>
</div>
</div>
</div>
</section>
Here it can be seen live: [I removed the link for privacy issues] at the "What we do" section.
Every .pic1,2,3 's transition-duration: 1s;
is interfering for what you see as the parallax for the backgroundPosition
. Use only the duration for the properties that actually need one, otherwise the default is all
. And you don't want that for the background's position. So use only the one for filter
:
also, don't copy/paste code in CSS!
Use a single rule like
/* no need for .pic1, pic2, pic3 { as well */
.pic {
cursor: pointer;
background-size: cover;
width: 30%;
padding: 32px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: white;
transition: filter 1s; /* add transition only to filter! */
}
.pic1 {
background-image: url(https://www.rgu.hu/CMF/jpg/laser-sci1.jpg);
}
.pic2 {
background-image: url(https://www.rgu.hu/CMF/jpg/life-sci1.jpg);
}
.pic3 {
background-image: url(https://www.rgu.hu/CMF/jpg/data-sci1.jpg);
}
and obviously add that class to all your HTML pic elements:
class="pic pic1 parallax-image"