Jump to content
Search Community

Scroll Trigger Animation problems [newie]

Pau Ferrer
Moderator Tag

Recommended Posts

Pau Ferrer
Posted (edited)

Hi there!! 👋🏻

I am using GSAP for the first time and I am loving it!! But I would do with some help on a slider like animation I am creating for a client.

This is done in Webflow using Saddle Framework. I will share Read Only link at the bottom of the message, but first some context.

The client wants a slider with 2 sets of cards that animates on scroll. We use a switch to go from one slider to the other and here is where I am having a problem...

When I click on the switch a new timeline gets set creating a timeline hell situation that scales the more you switch between the 2 sliders...

I've tried to kill the tweens with ".killTweenOf()", to use the timeline object and kill it to reset it, and nothing seams to work...

Here is my JavaScript:

// wait for DOM and scripts to load
window.addEventListener('load', ()=> {
	// GSAP Services Slider Switch Animation
  const section = document.querySelector('[bs-servicesSlider-element="section"]')
  const switchButton = section.querySelector('[bs-switchButton-element="switch"]')
  const toggle = switchButton.querySelector('[bs-switchButton-element="toggle"]')
  const leftText = switchButton.querySelector('[bs-switchButton-element="leftText"]')
  const rightText = switchButton.querySelector('[bs-switchButton-element="rightText"]')
  const retailerTrack = document.querySelector('[bs-servicesSlider-element="retailerTrack"]')
  const brandTrack = document.querySelector('[bs-servicesSlider-element="brandTrack"]')
  const items = [...document.querySelector('[bs-servicesSlider-element="list"]').children]
  const retailerItems = items.filter(item => item.children[1].innerHTML === 'Retailer')
  retailerItems[retailerItems.length - 1].style.borderRight = "none"
  retailerTrack.append(...retailerItems)
  const brandItems = items.filter(item => item.children[1].innerHTML === 'Brand')
  brandItems[brandItems.length - 1].style.borderRight = "none"
  brandTrack.append(...brandItems)
  const retailerProgressBar = section.querySelector('[bs-servicesSlider-element="retailerProgressBar"]')
  const brandProgressBar = section.querySelector('[bs-servicesSlider-element="brandProgressBar"]')
  let click = 0
  // GSAP Service Slider Scroll Animation
  const commons = { duration: .4, ease: "circ.inOut" }
  const scrollTriggerCommons = { start: 'top 25%', end: '200% 25%', scrub: true, markers: true, toggleActions: 'play pause reverse pause' }
  const retailerStrollTrigger = { trigger: retailerTrack, ...scrollTriggerCommons }
  const brandScrollTrigger = { trigger: brandTrack, ...scrollTriggerCommons }
  const setSliderAnimation = () => {
  	const track = click === 0 ? retailerTrack : brandTrack
    const progressBar = click === 0 ? retailerProgressBar : brandProgressBar
    const scrollTrigger = click === 0 ? retailerStrollTrigger : brandScrollTrigger
    const trackPosition = window.innerWidth - track.offsetWidth
    gsap.to(track, { 
      x: trackPosition, 
      ease: commons.ease,
      scrollTrigger: scrollTrigger
    })
    trackPosition === 0 ? progressBar.parentElement.classList.add('u-hide') : progressBar.parentElement.classList.remove('u-hide')
    gsap.to(progressBar, { 
      width: progressBar.parentElement.offsetWidth, 
      ease: commons.ease,
      scrollTrigger: scrollTrigger
    })
  }
  // GSAP Toggle Animation
  const toggleAnimation = () => {
  	gsap.to(toggle, { xPercent: click === 0 ? 0 : 100,  })
    setTimeout(() => {
      if(click === 0) {
        rightText.classList.add('u-hide')
        leftText.classList.remove('u-hide')
      }
      if(click === 1) {
        leftText.classList.add('u-hide')
        rightText.classList.remove('u-hide')
      }
    }, 200)
  }
  // GSAP Theme Animation
  const theme = { lightGrey: '#f0f0f0', darkGrey: '#232323', yellow: '#e8f83d' }
  const themeAnimation = () => {
  	gsap.to(section,
      { backgroundColor: click === 0 ? theme.yellow : theme.darkGrey, color: click === 0 ? theme.darkGrey : theme.lightGrey, ...commons })
    gsap.to(section.querySelector('.subheading_line'), 
      { backgroundColor: click === 0 ? theme.darkGrey : theme.lightGrey, ...commons })
    gsap.to(switchButton, 
      { backgroundColor: click === 0 ? theme.darkGrey : theme.yellow, color: click === 0 ? theme.lightGrey : theme.darkGrey, ...commons })
    gsap.to(toggle, 
      { backgroundColor: click === 0 ? theme.lightGrey : theme.darkGrey, color: click === 0 ? theme.darkGrey : theme.lightGrey, ...commons })
    gsap.to([...retailerTrack.children, ...brandTrack.children], 
      { borderColor: click === 0 ? theme.darkGrey : theme.lightGrey, ...commons })
  }
  // GSAP Switch Click Animations
  const changeTrack = () => {
    gsap.to(click === 0 ? brandTrack : retailerTrack, { opacity: 0, ...commons })
    if(click === 0) {
    	brandTrack.classList.add('u-hide')
      retailerTrack.classList.remove('u-hide')
      retailerProgressBar.classList.remove('u-hide')
      brandProgressBar.classList.add('u-hide')
    } else {
    	retailerTrack.classList.add('u-hide')
      brandTrack.classList.remove('u-hide')
      retailerProgressBar.classList.add('u-hide')
      brandProgressBar.classList.remove('u-hide')
    }
    gsap.to(click === 0 ? retailerTrack : brandTrack, { opacity: 1, ...commons })
  }
  switchButton.onclick = (event) => {
    event.preventDefault()
    click = click === 0 ? 1 : 0
    toggleAnimation()
    themeAnimation()
    changeTrack()
    gsap.to(window, {
    	...commons,
    	scrollTo: { y: "#service-slider_outer", offsetY: 80, autoKill: true },
    })
    gsap.killTweensOf([retailerTrack, brandTrack], "x")
    setSliderAnimation()
  }
  // Resizing function
  const displayedCards = { desktop: 3, tablet: 2, mobile: 1 }
  const resizeTrack = (screenWidth, track) => {
  	let cardsDisplayed = displayedCards.desktop
    if(screenWidth <= 991) { cardsDisplayed = displayedCards.tablet }
    if(screenWidth <= 568) { cardsDisplayed = displayedCards.mobile }
  	track.style.width = `${(screenWidth / cardsDisplayed) * track.children.length}px`
  }
  // Init Animation
  resizeTrack(window.innerWidth, retailerTrack)
  resizeTrack(window.innerWidth, brandTrack)
  changeTrack()
  themeAnimation()
  setSliderAnimation()
  window.onresize = () => {
  	resizeTrack(window.innerWidth, retailerTrack)
  	resizeTrack(window.innerWidth, brandTrack)
  	themeAnimation()
    changeTrack()
    setSliderAnimation()
  }
})

You can find the elements in the Webflow project under "services-slider_outer", and the first section inside the inner wrapper has the JS object with the current code.

Please, I need help as this is a key element of the project and I cannot create this animation with Webflow's animations since I need to dynamically change the width of the scroll animation.

Thanks in advance!!!

 

Link: https://preview.webflow.com/preview/buzket-space-v2?utm_medium=preview_link&utm_source=designer&utm_content=buzket-space-v2&preview=ff7a7c3ed6298bc1e243bd83225520d8&workflow=preview

Edited by Pau Ferrer
Forgot the link to the project...
GSAP Helper
Posted

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen.

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Posted

Hi,

 

Here is a simple demo of a content slider:

See the Pen VYZwvza by GreenSock (@GreenSock) on CodePen.

 

Hopefully this helps

Happy Tweening!

Pau Ferrer
Posted

Thanks for that!!

I am currently working on a CodePen example to illustrate the issue as recommended.

Will post it here as soon as I have it ready.

mvaneijgen
Posted

Why don't you just create both sliders and ScrollTriggers at the same time and then just on click of the which set the one to opacity: 0 and the other to opacity: 1 don't toggle the whole ScrollTrigger logic. This means both animations will be playing no matter what, but nobody would see this because they are not visible. 

 

I would also not have separate ScrollTriggers for the track and the progress bar, just have a timeline with a ScrollTrigger to control that whole setup and thus duplicate the progress bar for the second "slider", I did a quick proof of concept and you can for sure use a loop to create the to timeline with ScrollTrigger logic.

 

Hope it helps and happy tweening! 

 

See the Pen QwWKPQO?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen.

Pau Ferrer
Posted

Thanks @mvaneijgen!!

That made lots of sense and I managed to fix it thanks to your advise!!

Here is the final solution as I needed the switch toggle to animate and also to not refresh the page on click event...

So I targeted the tracks parents to hide/show and the slider mask as the scroll trigger so the start and end markers match!!

Thanks a lot!!

And thanks to GSAP for this amazing tool!! I am enjoying it very much!! And not coming back!!

Link here:

See the Pen dPypZOj by Pau-Ferrer-the-animator (@Pau-Ferrer-the-animator) on CodePen.

Pau Ferrer
Posted

Hey @mvaneijgen... sorry to bring this issue back, but the start and end points are getting refreshed in mobile when I scroll up fast and the address bar shrinks and this is making my animation to not work again...

I show a potential solution in another post using this code:

ScrollTrigger.config({
  autoRefreshEvents: "visibilitychange,DOMContentLoaded,load" // notice "resize" isn't in the list
});

But it is not working for me...

I am using Chrome in a iOS phone...

I also tried this without success...

// only fire callbacks when the active state toggles
ScrollTrigger.config({
  ignoreMobileResize: true,
});

I cannot represent the issue in a CodePen project since it is particular of mobile phones... sorry

I can provide the staging URL I am using to test development tho.

https://buzket-space-v2.webflow.io/

Thanks in advance!!!

Pau Ferrer
Posted

If I could I would give you a hug @mvaneijgen!!!

That worked beautifully!!!

And I didn't know you could use CodePen for mobile debuging!! Thanks for that tip!!

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...