Jump to content
Search Community

fastScrolledEnd and preventOverlaps With D3

smackdab test
Moderator Tag

Recommended Posts

I'm using ScrollTrigger combined with d3.  Specifically ScrollTrigger is handling the scrollytelling aspects of firing off certain d3 animations on the page.  All is working great, except for problems that come up with fast scrolling.  I've set fastScrolledEnd and preventOverlaps to true in my scrollTrigger object, but it's having no effect.  Is that because the animations are d3 instead of gsap, or is there something I'm doing wrong?  Any suggestions, anyone?

Link to comment
Share on other sites

It's impossible for us to diagnose for sure without seeing a minimal demo, but yes if you're not using GSAP for the animations then the fastScrollEnd and preventOverlaps features won't do anything (those force the animations to a certain state...but if there's no attached GSAP animation, of course that can't work). 

 

If you want some more help, post a CodePen or Stackblitz that very clearly illustrates the problem with as little code and structure as possible. 

 

Please don't include your whole project. Just some colored elements and the GSAP code is best (avoid frameworks if possible).

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

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

 

If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.

 

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

Link to comment
Share on other sites

Thank you very much.  You've answered my question: I can't use fastScrollEnd and preventOverlaps with d3 animations.  No need for the massive effort that would have been associated with condensing my project down to something codepennable when the answer is this simple.  Much appreicated!

  • Like 1
Link to comment
Share on other sites

Thanks!  But honestly, the effort to condense my very large codebase down to something the community here could understand would be extremely time consuming.  Yes, I could build a toy example that has nothing to do with my actual code, but before I do,  can I just ask what you mean by "an accessible progress value"?  I'm using simple d3 transitions.  I don't know of a way to access the state of a d3 transition between its start and end, but is that what you mean?  Any d3 experts (I'm certainly not one) want to weigh in?

Here's what chatGPT had to say about the matter of accessing transition progress:
 

In D3, the transition API does not provide a built-in way to directly access the progress or status of a transition between its start and end points. The transition system in D3 is designed to abstract away the details of the intermediate steps and provide a smooth animation between the initial and final states.

However, you can work around this limitation by using the on() method provided by the transition object to attach custom event handlers. By combining on("start", handler) and on("end", handler), you can get some insights into the transition's progress.

Here's an example:

 
javascriptCopy code
// Create a transition 
var transition = d3.transition() 
	.duration(1000) 
	.on("start", transitionStart) 
	.on("end", transitionEnd); 

// Transition start event handler function 
transitionStart() { 
	console.log("Transition started"); 
	// You can perform actions or track the start time here } 

// Transition end event handler function 
transitionEnd() { 
	console.log("Transition ended")
    // You can perform actions or track the end time here
}
    
); // You can perform actions or track the end time here }

In the above example, when you start the transition, the transitionStart function will be called, and when the transition ends, the transitionEnd function will be called. Inside these functions, you can perform any actions you need or track the start and end times of the transition.

Keep in mind that this approach does not provide direct access to the current progress or status of the transition between its start and end points. Instead, it notifies you when the transition starts and ends. If you require more fine-grained control or access to the intermediate states, you may need to implement custom logic using timers or tweens.

Link to comment
Share on other sites

Hi,

 

In fact a codepen doesn't have to be a reduced portion of your project, just a simple D3 animation with some space to scroll. We just want/need to see how you're plugin D3 with GSAP and Scrolltrigger in order to know what could be the issue. I have zero experience with D3 so there could be something simple or maybe not, but without a small sample of what you're actually doing we can't tell.

 

Definitely the start and end callbacks from D3 don't help because ScrollTrigger is not waiting nor needing to know exactly when the animations start/end.

 

Are you using onEnter, onEnterBack, onLeave, etc. to trigger your D3 animation at certain scroll positions like this:

ScrollTrigger.create({
  trigger: ".element",
  start: "top top",
  end: "+=200%",
  onEnter: () => /* start D3 animation */,
  onEnterBack: () => /* start D3 animation */,
  onLeaveBack: () => /* reverse/reset D3 animation */,
});

That's basically the structure we need to see with a D3 animation, not the ins and outs of your project, just how are you implementing this.

 

Happy Tweening!

Link to comment
Share on other sites

Ok, ok, uncle.   Here's some code below.  Let's see if this helps.  The code shows three of the many steps that I'm using in my ScrollTrigger setup.  The first sets up the scrollytelling area on the page, the second two create d3 animated charts in that area.  As you can see, the actual d3 chart creation work happens in functions not shown here (drawHist and redrawHist).  There are transitions happening within the d3 functions that render some svgs (and also some transitions that you can see here in the ScrollTrigger calls).  Those transitions are the source of the fast scroll problems, I believe.

The setTimeout statements in the onLeave and onLeaveback properties of the first ScrollTrigger are basically my hacky workarounds for not having fastScrollEnd and preventOverlaps available to me.  They just wait for a second to let the animations catch up after a fast scroll past the scrollytelling area in either direction, and then put the animations into the state I want them to be in.  Obviously not an optimal approach.

Thoughts on getting fastScrollEnd and preventOverlaps to work here, anyone?  Big thanks for any help.
 

    ScrollTrigger.create({
        trigger: ".bar-chart-steps",
        start: "top 0%",  
        end: "bottom 80%", 
        pin: ".to-pin",  
        pinSpacing: false,
        onEnter: function() {
        },
        onLeave: function() {
            setTimeout(function() {
                d3.selectAll(".container1 .chartArea").remove()
                d3.select("#blahblah .chartTitle").text("blahblah")
                
            }
            , 1000)
        },
        onLeaveBack: function() {
            setTimeout(function() {
                d3.selectAll(".chartArea").remove()
                drawHist("foo", undefined, "large", 1);
            }, 2000)
        },
        markers: false,
    })

    ScrollTrigger.create({
        trigger: "#step1",
        start: "top top",  
        end: "bottom 80%", 
        onEnterBack: function() {
            console.log("enter back 1")
            redrawHist("larger");
        }
    })

    ScrollTrigger.create({ 
        trigger: "#step2",  
        start: "top 20%",  
        end: "bottom 80%",
        onEnter: function() {
            redrawHist("smaller"); 
        },
        onEnterBack: function() {
            d3.selectAll(".histDiv")
                .transition()
                .duration(1000)
                .style("opacity", 0)
                .on("end", function(){
                    d3.selectAll("svg").remove()
                    .transition()
                    .duration(1000)
                    drawHist("bar", 90, "large", 1);
                })
        }
    })

 

Link to comment
Share on other sites

Ah ok, this is largely what I was after
 

Quote

in D3, the transition API does not provide a built-in way to directly access the progress or status of a transition between its start and end points. 

 

That's not a great animation system really. With GSAP animations, everything is object based so you can use a tween to control the progress of another tween or timeline. If d3 had a progress value available in an object somewhere you could do the same thing. Use a tween to set the progress, which would essentially animate the animation.

 

let tween = gsap.to(..etc).pause()

gsap.to(tween, {
  progress: 1,
  duration: 3,
}

 

The only thing I can really recommend is re-writing your transitions out to use GSAP animations, then they'll be able to benefit from the control over timelines and tweens. I know that's probably a daunting prospect though. If the way you've got it working at the moment works then it works 🤷🏼‍♀️

Here's a demo in case it's useful for you or anyone in the future

See the Pen ZEmpdEP?editors=0011 by GreenSock (@GreenSock) on CodePen

  • Like 2
Link to comment
Share on other sites

Thanks, @Cassie. That's what I feared.  One thing that crossed my mind, and maybe this is what you're suggesting, is that I could build the objects (svgs, in my case) with d3, but control any animation (mostly opacity transitions, but also occasionally some movement of various elements that are part of the svgs) with gsap / ScrollTrigger.  Meaning that I'd use d3 to build stuff and ScrollTrigger to move that stuff around, replacing d3 transitions with gsap animations. That way I could get the benefit of d3's very granular chart building (which I love) as well as the benefit of scrollytelling capabilities with ScrollTrigger.  Does that make sense?  Would that work?

 

Link to comment
Share on other sites

Hi,

 

I think that is exactly what Cassie is achieving in the codepen example she posted. All the graph stuff is handled by d3 while the animation is handled by GSAP and ScrollTrigger, so I think that should work without any issues.

 

Happy Tweening!

Link to comment
Share on other sites

Aha!  Got it.  This makes perfect sense.  Huge thanks, everyone, for all this help.  I'll try what you've suggested, but I don't see I any reason why this approach wouldn't give me what I need in terms of fixing the fast scroll problem with fastScrollEnd and preventOverlaps.  Should be just what I need!  Thanks again.

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.
×
×
  • Create New...