Hello everyone,
I'm currently facing an issue on a website that features a horizontally scrolling section. I've set up the site so that when a user clicks on a year from a menu, the page correctly scrolls to the corresponding element in the horizontal scroll section. However, after this scroll occurs and the target is reached, if the user attempts to manually scroll again, the scroll position automatically jumps back to where it was before the menu item was clicked.
Here is a brief overview of the technologies and setup:
JavaScript Libraries: I'm using GSAP for animations, with plugins like ScrollToPlugin and ScrollTrigger. Additionally, I'm integrating Swiper for swipe functionalities and Lenis for smooth scrolling effects.
Behavior: Upon clicking a year in the menu, GSAP's ScrollToPlugin does its job to bring the target element into view. After reaching the target, any new user-initiated scroll action causes the page to revert to the original scroll position.
Expected Behavior: The scroll position should remain at the new location after scrolling to the target, and any further manual scrolling should be based on this new position.
I suspect there might be an issue with how the scroll position state is managed after a programmatic scroll, or perhaps there's a conflict between GSAP's scroll handling and Lenis's smooth scrolling. I've tried managing the update cycles of Lenis and ensuring the GSAP animations are synchronized with it, but the issue persists. I have also experimented with using flags and various methods to lock the scroll position temporarily, but nothing has resolved the issue so far.
I'm doing it in Webflow so you can see the problem here: https://las-vidas-que-nos-dejaron.webflow.io/
// GSAP HORIZONTAL SCROLL LOGIC
let isScrollingToYear = false // Add this line at the beginning of your script
let horizontalItem = $('.horizontal-item')
let horizontalSection = $('.horizontal-section')
let moveDistance
// Declare a variable to store the last scroll position
// eslint-disable-next-line no-unused-vars
let lastScrollPosition = 0
function calculateScroll() {
// Desktop
let itemsInView = 4
let scrollSpeed = 3
if (window.matchMedia('(max-width: 479px)').matches) {
// Mobile Portrait
itemsInView = 1
scrollSpeed = 1.2
} else if (window.matchMedia('(max-width: 767px)').matches) {
// Mobile Landscape
itemsInView = 1
scrollSpeed = 1.2
} else if (window.matchMedia('(max-width: 991px)').matches) {
// Tablet
itemsInView = 2
scrollSpeed = 1.2
}
let moveAmount = horizontalItem.length - itemsInView
let minHeight =
scrollSpeed * horizontalItem.outerWidth() * horizontalItem.length
if (moveAmount <= 0) {
moveAmount = 0
minHeight = 0
// horizontalSection.css('height', '100vh');
} else {
horizontalSection.css('height', '200vh')
}
moveDistance = horizontalItem.outerWidth() * moveAmount
horizontalSection.css('min-height', minHeight + 'px')
}
calculateScroll()
window.onresize = function () {
calculateScroll()
}
let tl = gsap.timeline({
scrollTrigger: {
trigger: '.horizontal-trigger',
start: 'top top',
end: 'bottom top',
markers: false,
invalidateOnRefresh: true,
scrub: 1,
},
})
tl.to('.horizontal-section .list', {
x: () => -moveDistance,
duration: 1,
})
And function to scroll to the element with the given year and month ID (years menu)
// Function to scroll to the element with the given year and month ID
function scrollToYear(year, month) {
const targetId = `#year-${year}-${month}`
const targetElement = $(targetId)
if (targetElement.length) {
// Set the flag to true
isScrollingToYear = true
console.log('Is scrolling to year enabled?', isScrollingToYear)
// Calculate the target scroll position
const scrollX = targetElement.position().left
console.log('Target scroll position:', scrollX)
// Log the current scroll position
console.log(
'Current scroll position:',
$('.horizontal-section .list').position().left
)
// Save the current scroll position
lastScrollPosition = $('.horizontal-section .list').position().left
// Animate the horizontal scroll to the target element's X position
gsap.to('.horizontal-section .list', {
x: () => -scrollX,
duration: 3,
ease: 'power3.inOut',
onUpdate: Lenis.update, // Assuming lenis.update is the method that needs to be called on update
onComplete: () => {
console.log(
`Smoothly scrolled to the year: ${year} and month: ${month}`
)
// Remove --is-active class from any other elements that might have it
$('.horizontal-section .list .--is-active').removeClass('--is-active')
$('.horizontal-month-item.--is-active').removeClass('--is-active')
// Add --is-active class to the target element
targetElement.addClass('--is-active')
// Format the month to have two digits
const formattedMonth = month.toString().padStart(2, '0')
// Find the corresponding .horizontal-month-item and add the --is-active class
const targetMonthItem = $(
`.horizontal-month-item[data-month="${formattedMonth}/${year}"]`
)
if (targetMonthItem.length) {
targetMonthItem.addClass('--is-active')
}
// Update the sticky title
gsap.to('.sticky_title', {
duration: 0.2,
autoAlpha: 0,
onComplete: function () {
$('.sticky_title').text(year)
gsap.to('.sticky_title', { duration: 1.5, autoAlpha: 1 })
// Log the current scroll position after the animation
console.log(
'Current scroll position after animation:',
$('.horizontal-section .list').position().left
)
// Set the flag back to false
isScrollingToYear = false
console.log('isScrollingToYear enabled?', isScrollingToYear)
// Update the move distance directly
moveDistance = -$('.horizontal-section .list').position().left
},
})
},
})
} else {
alert(`No element found with ID: ${targetId}`)
console.error(`No element found with ID: ${targetId}`)
}
}