Jump to content


  • Posts

  • Joined

  • Last visited

James135's Achievements

  1. James135

    Follow by mouse

    Still works great in 2022!
  2. Had to modify line 25 (see below) to make it deploy without it telling me that "clamp is not a function at Draggable", I'm guessing it just needed a refresh to trigger the refresh event listener if (clamp !== undefined) { horizontal_scroll.scroll(clamp(this.startScroll - (this.x - this.startX) * dragRatio)); } else { ScrollTrigger.refresh(); } Really annoying bug
  3. Update: I ended up coming across an issue where the fake pin would sometimes be slow, after every 3rd or so refresh of the page the animation would start stuttering, I decided to just sit and try to figure out what was causing it, spent two hours and I learned a lot instead of sending it to the back of the backlog. Was not GSAP related at all. Fake pinning worked flawlessly in the end If you're using Angular, make sure you use Renderer2 in Angular, the performance boost will be huge compared to any other alternative when applying transform. Just stream the scroll position via the onUpdate() of the animation, like I explained in my last post, send that over to the pinned element (I did it via bSubject, there are better ways out there as well) and apply the appropriate transform via Renderer2, it's crazy fast and it may be as good as the real thing Some variation of this may help, what this piece of code does is apply your streamed -> transformed offset value (x) to the #sticky element, simulating a sticky effect // use a variation of this, you can subscribe to the onUpdate, like via bSubject, but there are better alternatives, it just works for me let el = document.getElementById('sticky'); this.renderer2.setStyle( el, 'transform', 'translate3d(' + x + 'px,0px,0px)' ); })
  4. Essentially I made a fake pin, where I just stream the current scroll position (args.progress seems more accurate than args.scroll()) via the onUpdate() function in a scrolltrigger instance, I'll clean things up and post a codepen later as I'm still trying to fully understand what I did For those that may have trouble with this kind of thing, here's my advice/thoughts: Initially scrolltrigger seemed very abstract when trying to make it work with "real" pins, it was like learning a new language, things wouldn't stick, or wouldn't trigger. Then, I tried to make scrolltrigger instances work for me, and I learned I could create one on any DOM element, and it would spit out useful data, I realized you can do any DOM manipulation like that, and it brought me back to more familiar territory and stuff I had seen with Locomotive Scroll. At that point I stopped trying to follow the examples and just tried it myself and things just worked, because once you have real up to date data on your DOM elements and which position you are in the page's scroll, you can make use of a million tools, like clamp functions from lodash (I think GSAP has a clamp util too) and so many other tools. I don't know it was like a light clicked like "Ohh okay that's how you use scrolltrigger". I'll post a pen (or preferably stackblitz) with a more concrete example of what I mean, but my advice is to not be afraid of fake pinning.
  5. Made a custom workaround (sloppy) using the current scroll position, might update the Pen if there's interest. The question still stands however as I haven't seen this specific example using horizontal scroll and draggable with a sticky element, might be useful if a pro figures out how to do it the right way
  6. Workaround: ScrollTrigger.addEventListener("scrollEnd", () => { console.log('done') }); I updated the Pen
  7. Take a look at this Pen's JS, the onComplete function should be getting triggered, but it isn't. However, the onUpdate is being triggered. What could be causing this?
  8. Take this Pen How do you go about sticking something (say the 3rd 'text' box) inside this so that it stays stuck to the left side of the screen once you reach it? https://codepen.io/akapowl/pen/KKqpVza/753c9839a59e4cb1e0ca7f90fa064513
  9. Hey @GreenSock, just saw your reply! I actually ended up dropping Locomotive Scroll for this current project I'm working on, I found that GSAP and its plugins were better suited for the specific project's use case, though Loco has great smoothing. GSAP's performance was better as well, since the smoothing wasn't as buttery, although I ended up finding GSAP's smoothing plugins, they may work just as good and are probably faster. To those that may end up working on a project that can't switch from Locomotive Scroll, I dug up my solution from github. I should have made a StackBlitz at the time to make things more obvious, but either way I was able to resolve the problem with the following unloading method: unload() { console.log("Kill", this.horizontal_scroll); // check locomotive scroll instance variable binding this.horizontal_scroll.destroy(); // destroy instance of locomotive scroll ScrollTrigger.removeEventListener("refresh", this.horizontal_scroll.update()); // remove listener, note this triggers a console.log error, but it works ScrollTrigger.scrollerProxy(null); // <- this will get rid of the current scrollerProxy ScrollTrigger.clearScrollMemory(); this.horizontal_scroll = null; // destroys locomotive scroll instance variable binding ScrollTrigger.getAll().forEach(t => t.kill()); } There's many ways to use it, but I used it in the same component as I declared the locomotive scroll instance. You call it right before changing routes/destroying the component that holds your locomotive scroll instance.
  10. Anyways so I was able to stitch together a function to bring it to 0 when it reaches the scroll position of 1, and another to bring it to the actual end of the container when it reaches end-1px. The game changer was Greensock's solution which created stability between refresh/renders. It'll always stop 1px after the start and 1px before the end Hope this helps
  11. It's really interesting how you clamped it, and you're getting closer to the actual and full fix for this problem. Because my sloppy clamping method was this, but it was not stable, it would keep changing every 3rd or 4th time I hit refresh. I'll try to combine your clamping method with this: As soon as it hits horizontalScroll.end - 1, I'll tell it to scrollTo to the actual end to not have the ugly 1px on the left, hope it works, but it may not.
  12. Hi, I did some research on this forum about GSAP's Draggable and horizontal scrolling, and I keep seeing this Pen (see below) brought up every time, but nobody seems to ask the obvious question about dragging past the end bound and how the view snaps up when you try to return to the draggable area. To recreate the behavior, try to drag past the end or before the beginning of the scrollable container. I wondered, what is the professional way to resolve this? Maybe with Bounds? So I tried it with the following bounds in my draggable instance declaration: let scroll_container = document.querySelector(".scroll_container"); let scroll_container_width = <number>scroll_container.offsetWidth; ... Draggable.create(".proxy", { ... inertia: true, bounds: { maxX: 0, minX: -(scroll_container_width - window.innerWidth), }, Outcome: the draggable sometimes goes before the start, but sometimes allows itself to go 1px past the end. I looked more, found the following properties that I thought would help: edgeResistance: 1, overshootTolerance: 0, They didn't help much, it rarely ever goes 1px before the scroll start, but it sometimes goes 1px past the end. When it does, I hit refresh, and most of the time it stops itself at the end and it respects the bounds. I even ran a little survey just now, and I tried reloading and dragging past the end, it worked as expected until the 4th reload then it went 1px past the end, so annoying. A temporary fix is below it never scrolls past the edge, but it's sloppy. let scroll_container = document.querySelector(".scroll_container"); let scroll_container_width = <number>scroll_container.offsetWidth; ... Draggable.create(".proxy", { ... inertia: true, bounds: { maxX: 0, minX: -(scroll_container_width - window.innerWidth - 1), }, Why is this not addressed in the example Pen (below) that is always referenced? How can this be resolved? Update 1: Problem is related to GSAP's ability to decide when the scroll progress reaches 100%, sometimes it decides it's 1px bigger than the container bounds. It's not related to DOM rendering either, you can create the draggable instance 10 seconds after the DOM renders, problem may or may not appear. It's a moody bug! Furthermore, @OSUblake tried to tackle this, and it didn't seem to be fruitful. Update 2: In my tests, I was able to find that the scroll value where the scroll progress is 100% is not calculated once with this draggable Pen, it's dynamically calculated and it's related to the inertia and how hard you drag it. If you drag it slow enough, it'll respect your boundary, if you drag it fast, it might go past it by 1, you can try it 100 times and each time it'll calculate a new value for scroll progress 100%. I'll fork a pen if I have the time. The solution I think would be most stable: when the scroll instance reaches let's say 1px from the end edge, stop the inertia completely and replace it with a scrollTo to the end edge. I'll update as I go along, the problem is that it keeps changing all the time, it's hard to reproduce and isolate the problem..
  13. Just stumbled on this from a long, long search. This is apparently a widespread problem with questions being asked from Stackoverflow, github, to this very forum.. It's one of the toughest debugs I've ever tackled for something that should be kind of simple right? To kill scrolltrigger, to then reinitialize it. Even the GSAP devs think the solution is as simple as the below line inserted some say before executing the route change, some say before reinitializing, etc etc. ScrollTrigger.getAll().forEach(t => t.kill()) The only workaround all would agree is unacceptable, you can find it here, it's to replace the [routerlink] or whatever you are using to route, into an href, which breaks the purposes of an SPA. What's going on with route changes? I've seen this same problem with Next.js users, Vue users, React users, and Angular users. The worst part is that it's a very fickle problem, it's really specific and nothing seems to change anything once scrolltrigger wraps its arms around your code. I just can't find what the problem is. I'm hitting it from every side... I'll keep trying to debug this, but it's very strange! Edit 1: I suspect it's related to GSAP's event listeners; by a process of elimination, going from one line of code to the next where I initialize GSAP, the one line that reacts at all to route changes is ScrollTrigger.addEventListener("refresh", () => sample_function()); Furthermore, when you check here, an engineer at a Belgian company seemed to have discovered a similar issue by accident where the event listeners are not cleaning themselves up when the instances is killed.. I tried to reproduce this: I killed all my scrolltrigger instances via ScrollTrigger.getAll().forEach(t => t.kill()) then I resize my browser, and lo and behold, the listener just picked up a refresh event from the 'dead' instance. While researching I found this nearly identical problem with difficulty to kill the instance Final edit: Found a semi-clean working example that worked for me when I copy the exact code they have going on, it'll do the job for now but she's messy. I hope this post can serve as light to anyone lost with this problem, it's definitely not easy To devs that might find this, the scrolltrigger event listener is still listening, you can see for yourself in a console.log with a resize event listener from the other route, it's just a tentative workaround