Jump to content
GreenSock

jonofyi

How to target onEnter and onBackEnter events with ScrollTrigger

Go to solution Solved by Carl,

Recommended Posts

I have a demo (attached) that has a series of ScrollTrigger enabled <div />s superimposed on a Three.js scene. I'd like to use a some of the superimposed elements to block the Three.js scene and "cut" to a different set of objects. I was able to achieve this by using timeline.eventCallback('onUpdate', ...), but it runs way more than it needs to. Also, sometimes on first scroll down the cube and sphere do not swap (I believe this is because both sets of timelines have eventCallbacks that are conflicting).

 

Is there a way to target just onEnter and onBackEnter events? I tried passing onEnter as a string to the eventCallback, but I don't think it gets called.

See the Pen yLKggyo by jonobr1 (@jonobr1) on CodePen

Link to comment
Share on other sites

  • Solution

Cool demo. I can't really dive into the three js or anything, but keep in mind that onEnter and onEnterBack are callbacks for ScrollTriggers not timelines. I didn't see them anywhere in your code so I don't know what you have tried.

 

Please consult this demo here that shows how to use these event callbacks to change the text. They only fire once each time they are needed so hopefully this helps

 

See the Pen qBrRaeX?editors=1010 by GreenSock (@GreenSock) on CodePen

  • Like 1
Link to comment
Share on other sites

Ahh, got it. Thank you. I need to put the callbacks in the constructor of the timeline under the scrollTrigger options because they are scrollTrigger events, not Timeline events. I hadn't used onEnter in my code because I got it to work with the eventCallback('onUpdate', ...). But, it's redundant because it's being called more than it needs to be.

 

I see now that all plugin related things are scoped to the scrollTrigger options. For some reason, that wasn't clear to me before.

 

Tangentially, it would be great if you could chain events in the eventCallback like so: eventCallback('onStart onComplete', ...); Is the forum the right place to request new features?

Link to comment
Share on other sites

I updated the codepen with the correct usage and left commented out the onUpdate code I had originally posted with.

Link to comment
Share on other sites

glad it worked.

 

yup, if you want to request a new feature the forums are great for this. 

 

I'd suggest a new thread that concisely explains the problem with the existing API and how your suggestion would make things better for users.

 

New ideas are always welcomed but the team always has to consider things like: impact on file size, number of users it will benefit, whether or not the same results can be achieved with the existing api, whether or not legacy code will be affected, etc. 

Link to comment
Share on other sites

Yep, it looks like you figured it out. Nice work. 🎉

 

58 minutes ago, jonofyi said:

I see now that all plugin related things are scoped to the scrollTrigger options. For some reason, that wasn't clear to me before.

 

So are you saying that you didn't feel like the docs made it clear enough that the onEnter/onLeave/onEnterBack/onLeaveBack belong in the ScrollTrigger object? I definitely want to make improvements where necessary in the docs. It's always a bit of a balancing act with keeping them clear, concise, and detailed enough. 

 

52 minutes ago, jonofyi said:

it would be great if you could chain events in the eventCallback like so: eventCallback('onStart onComplete', ...); Is the forum the right place to request new features?

You want to make eventCallback() accept a space-delimited list of callbacks that would all point to the same function? 

 

Please keep in mind that in the VAST majority of cases, developers just define their callbacks on the vars object, like:

let tl = gsap.timeline({
  onStart: func,
  onComplete: func,
  onUpdate: func
});

It's super rare for anyone to use eventCallback() in the first place. The vars object is much cleaner. The only time eventCallback() is useful is when for whatever reason it's impossible for you to declare things on the vars object in the constructor. It's also quite uncommon for developers to assign the same callback to multiple types (onStart/onComplete/onUpdate).

 

So this proposed features seems like it'd serve well under 0.001% of our users which makes it tough to justify the kb and added API surface area. I'm not saying it's a "bad" idea at all. Totally makes sense in some situations...I'm just saying it's my job to be protective of the file size and API surface area. And it's not as if it's not possible to get the desired functionality with the existing API - it just might require very slightly more code. Did you know you can chain calls like animation.eventCallback(...).eventCallback(...)? 

 

Thanks for providing the minimal demo, by the way. 🙌

Link to comment
Share on other sites

Quote

So are you saying that you didn't feel like the docs made it clear enough that the onEnter/onLeave/onEnterBack/onLeaveBack belong in the ScrollTrigger object?

I think what semantically confused me is that there are two ways to make ScrollTrigger tweens. One that is from the ScrollTrigger namespace and one that "overrides" the core methods through the scrollTrigger object. I put that in quotes because it's likely not what happens, but that was my assumption watching the video on ScrollTrigger and perusing the documentation page while I was making my client's project.

 

Quote

Please keep in mind that in the VAST majority of cases, developers just define their callbacks on the vars object

I respectfully disagree. This is my first time using gsap, so clearly I have a learning curve and my opinion is only from a seasoned developer coming with a fresh set of eyes onto a mature library. But, in react it's memory efficient to declare all of your objects on mount and then to use and modify them as needed. A prime example (in pseudocode) is this:

 

import React, { useEffect, useRef, useState } from 'react';
// Assume gsap is loaded

export default function App() {

  const domElement = useRef();
  const refs = useRef({ timeline: null });
  const [width, setWidth] = useState(window.innerWidth);
  const [height, setHeight] = useState(window.innerHeight);

  useEffect(setup, []);
  
  function setup() {

    refs.current.timeline = gsap.timeline();
    window.addEventListener('resize', resize, false);
    return unmount;
    
    function unmount() {
      window.removeEventListener('resize', resize, false);
    }
    function resize() {

      const w = window.innerWidth;
      const h = window.innerHeight;

      setWidth(w);
      setHeight(h);

      timeline.clear();
      // Change animation logic based
      // on viewport dimensions (i.e portrait / landscape)
      if (w > h) {
        // Move horizontally
        timeline.to(domElement.current.style, { left: 25 });
      } else {
        // Move vertically
        timeline.to(domElement.current.style, { top: 25 });
      }
    }
  }

  return (
    <div ref={ domElement } className="app" />
  );

}

Perhaps the gsap way is to destroy the timeline and make a new one every time the page resizes? In the project I was working on (which brought me to raise these questions), the eventCallback method added flexibility for this use case. Though, it was unable to handle ScrollTrigger events like onEnter.

Link to comment
Share on other sites

15 minutes ago, jonofyi said:

I think what semantically confused me is that there are two ways to make ScrollTrigger tweens. One that is from the ScrollTrigger namespace and one that "overrides" the core methods through the scrollTrigger object. I put that in quotes because it's likely not what happens, but that was my assumption watching the video on ScrollTrigger and perusing the documentation page while I was making my client's project.

Hm, I read that a few times and I'm a little fuzzy on what you mean. 

 

Perhaps it'd help if I clarify...

let tl = gsap.timeline();
tl.to(...).to(...);

ScrollTrigger.create({
  animation: tl,
  ...
});

Produces exactly the same result as the following, which likely seems more intuitive to some people:

let tl = gsap.timeline({
  scrollTrigger: {
    ...
  }
});
tl.to(...).to(...);

We provide conveniences like that where it makes sense.

 

There is no "overriding" that I can think of, but maybe I'm misunderstanding what you meant. 

 

20 minutes ago, jonofyi said:

I respectfully disagree. This is my first time using gsap, so clearly I have a learning curve and my opinion is only from a seasoned developer coming with a fresh set of eyes onto a mature library. But, in react it's memory efficient to declare all of your objects on mount and then to use and modify them as needed.

I'm trying to understand where in your code you felt it was necessary/helpful to use eventCallback() rather than declaring the callbacks in the vars object. Could you explain please? 

 

23 minutes ago, jonofyi said:

Perhaps the gsap way is to destroy the timeline and make a new one every time the page resizes?

It is actually cheaper sometimes to just create a new one rather than trying to invalidate() and reuse the same instance every time. But in certain cases it does make sense to reuse. 

 

24 minutes ago, jonofyi said:

the eventCallback method added flexibility for this use case.

Cool, you should totally use that if it helps you. I didn't mean to discourage you from using eventCallback(). I'm simply saying in my 14+ years doing this, spending tens of thousands of hours in these forums helping people, I almost never see developers using eventCallback(), that's all. 

 

It's always valuable for us to hear from seasoned developers like you who are picking up the tools for the first time and sharing about any rough spots they encountered or misunderstandings based on reading the docs or watching the video(s). 

 

We've got some features coming in the next release that I think you're gonna love. Stand by for that. 🤫

Link to comment
Share on other sites

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.
×