Jump to content
Search Community

Leaderboard

Popular Content

Showing content with the highest reputation since 04/21/2024 in all areas

  1. Indeed without a minimal demo seeing the code in action it is hard to help you debug. That said have you seen my post on how to animate all types of different clip masks? Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/jOdJGQq
    4 points
  2. Yeah that is an easy fix, check out the video below if you need any help
    3 points
  3. GSAP is highly optimised and will get it's initial values on page load, but because you tween the same element twice they both get the same starting position and because you want the second (ScrollTrigger) animation to wait for the first one to end you have to tell it to that tween some how. Luckily GSAP has you covered with immediateRender: false, if you put that on the second tween everything works as expected. Hope it helps and happy tweening! https://stackblitz.com/edit/stackblitz-starters-cgdvlq?file=components%2FBanner.tsx
    3 points
  4. Welcome to the forum. I'd probably use a yoyo and repeat:1 for the opacity in/out. Also making sure the ease is set to none as this is a scrub:true situation. https://codepen.io/PointC/pen/dyLwoej Hopefully that helps. Happy tweening and welcome aboard.
    3 points
  5. Yep, same issue - you're creating things out of order, thus they refresh in the wrong order. For example, let's say elementA is 100px from the top of the screen, and there's a ScrollTrigger that triggers when that hits the top of the screen ("top top"). So normally, the start would be 100. But what if there's another ScrollTrigger that pins an element above that one for 1000px - that'd push everything down, thus that element should trigger at 1100px instead of 100px. If ScrollTrigger calculates them in the wrong order, it'd set the first one to a start of 100px (because the pinning one hasn't been factored in yet). Here's a helper function that you can call after all of your elements are in place, and it'll order things based on their proximity to the top of the viewport: function verticalSort() { let scroll = window.pageYOffset; ScrollTrigger.getAll().forEach(t => t._sortY = t.trigger ? scroll + t.trigger.getBoundingClientRect().top : t.start + window.innerHeight); ScrollTrigger.sort((a, b) => a._sortY - b._sortY); } https://codepen.io/GreenSock/pen/ZEZPqyd?editors=0010 Better? Of course you could solve everything by explicitly stating the unique refreshPriority for each, but the above function seemed easier and it should work in most cases.
    2 points
  6. I'd also recommend using a unique class as @Rodrigo suggested. You can, however, make it work with the original HTML by using a child combinator to choose only the direct descendants of the header-icons class. gsap.to('.header-icons > div', { Happy tweening.
    2 points
  7. Hi @spotipre welcome to the forum! The angled black part you can do with a clip-path animation, see simple demo below https://codepen.io/mvaneijgen/pen/MWLxyPp And the weird image effect you can probably do with something like Pixi.js. Keep in mind that this question is kinda outside of the scope of these forums, we like to focus on GSAP specific questions, so if you still need help be sure to post a minimal demo focused on an issue with one of the GSAP tools. Hope it helps and happy tweening! https://pixijs.com
    2 points
  8. Are you sure you've updated your pen? It is always best to relink the pen or even fork, so that in the thread we can see the progress of the current version of that time. To me it seems like you've removed the functions from the parameters, this is importent, because it indicates to GSAP that is can recalculate the values, if you leave this out you tell GSAP get the value once and never update it! // From height: gsap.utils.random(10, 100, true), // Is already a function width: gsap.utils.random(0, 100) + "%", // not a fucntion // To height: () => gsap.utils.random(10, 100, true), // Better save and also convert it to a function width: () => gsap.utils.random(0, 100) + "%", // Convert to function Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/XWQOGpa?editors=0010
    2 points
  9. Oh yes, that's exactly what i was looking for ... wouldn't have guessed i could morphSVG from a class name to another class name. Best library ever! (I promise to learn to use codepen ... it's just firefox obfuscates referrers and these embeds cause all kind of warnings, so i'm somewhat reluctant, but that's my problem to solve.)
    2 points
  10. hi @alexr8 maybe these 2 demos help 1 with draggable and 1 without https://codepen.io/GreenSock/pen/BaQXjWw?editors=0010 https://codepen.io/GreenSock/pen/RwKwLWK
    2 points
  11. Your video is around 15 seconds long, so I've split it up in three sections of each 5 seconds. First of the best thing to do when working with ScrollTrigger is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. This way you can focus on one part at a time and it will save a lot of headache when debugging. I've enabled GSDevTools for the animation, so we can first fully focus on the animation and create what we want to happen on scroll. I've modified your HTML and CSS, I've created a .trigger element and have everything stack right on top of each other with CSS (please disable all the JS to see what it looks like) I've taken this logic from my stacking cards post I encourage you to read through it, see below Then on the timeline I've put all the tweens, frist the video tweens to 5 seconds over a duration of 5 seconds, then the video tweens to 10 seconds over a duration of 5 seconds and at the same time the first card animates in from of screen, and them the same for the next card. This is probably not fully correct, but it shows you how you can create a timeline and have things happen at the same time. I've add some ScrollTrigger code but this is commented out, but you can enable it to see what this would do on scroll, but be sure to disable it again when you want to tweak the animation. If you're new to GSAP check out this awesome getting started guide https://gsap.com/resources/get-started/ and check out this awesome tutorial how to work with ScrollTrigger. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/bGJOQzq?editors=1010
    2 points
  12. You where almost there! I've move your timeline outside the loop and add the ScrollTrigger logic to the one timeline, then I've add all your tweens to that one timeline and let ScrollTrigger control it. Does that make sense? Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/qBwLJXX?editors=1010
    2 points
  13. Hi, I think the main issue here is the fact that you're using the same element as the scroller (the document element). ScrollTrigger allows you to define a scroller which is the element where ScrollTrigger will look for the scroll position in order to update any Tween/Timeline you pass to it. From the ScrollTrigger docs: scroller String | Element - By default, the scroller is the viewport itself, but if you'd like to add a ScrollTrigger to a scrollable <div>, for example, just define that as the scroller. You can use selector text like "#elementID" or the element itself. https://gsap.com/docs/v3/Plugins/ScrollTrigger/#config-object Here are a couple of demos that use a similar approach: https://codepen.io/GreenSock/pen/yLRQozy https://codepen.io/GreenSock/pen/OJBvBjB Hopefully this helps. Happy Tweening!
    2 points
  14. Although this didn't feel intuitive to me in this scenario, autoScroll did fix the issue. Thanks a lot!
    2 points
  15. Hi @Sikriti Dakua welcome to the forum! What is the part you're having trouble with? In your example you have different coloured sections which each there own text, but in the example you share all the text is all just stacked right on tof of each other. If that is what you want check out my card stacking tutorial of course swap out the cards for just text, but the logic should be the same. With CSS make all the text stack right on top of each other and then just animate them in and out one by one. Also keep in mind the best thing to do when working with ScrollTrigger is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. This way you can focus on one part at a time and it will save a lot of headache when debugging. Hope it helps and happy tweening, but if you still need help be sure to post back here with what you've tried!
    2 points
  16. The useGSAP hook has a dependency for at least React 16, but it doesn't use/install React 16: "dependencies": { "gsap": "^3.12.5", "react": ">=16" }, If you already have React 18 installing the @gsap/react package won't change a thing and won't install another version of React. I just tested this on a demo here on my local machine using the latest version of Next without any issues. We only encountered some issues while importing the @gsap/react package from esm.sh, which solves by doing this: import React from "https://esm.sh/react@18.3.1"; import ReactDOM from "https://esm.sh/react-dom@18.3.1"; import gsap from "https://esm.sh/gsap"; import { useGSAP } from "https://esm.sh/@gsap/react?deps=react@18.3.1"; We'll update the dependencies on the hook to be at least version 17 of React. Finally it would be super helpful if you could create a minimal demo that reproduces this error. Happy Tweening!
    1 point
  17. Hi @explorerzip, In Blake's demo all you have to do is add a repeatDelay value: https://gsap.com/docs/v3/GSAP/Timeline/repeatDelay() Something like this: var tl = gsap.timeline({ paused: true, repeat: -1, repeatDelay: 2, // Two seconds between animations onRepeat: updateImages, onUpdate: updatePath }) .to(arc, { duration: 3, end: 360, ease: "none" }) Hopefully this helps. Happy Tweening!
    1 point
  18. Hi, You can use Stackblitz to create a Nuxt demo and the GSAP Trial package in order to use the bonus plugins on Stackblitz: https://stackblitz.com/ https://www.npmjs.com/package/gsap-trial Here is a simple Nuxt demo: https://stackblitz.com/edit/nuxt-starter-vzsxyp?file=app.vue Hopefully this helps. Happy Tweening!
    1 point
  19. Hi @fraserYT and welcome to the GSAP Forums! You are using an extremely generic selector. I'd strongly recommend you to use a unique class in your elements even if it doesn't have any styles, just for selecting purposes. This seems to work the way you expect: <div class="header-icons"> <div class="menu-item"></div> <div class="menu-item"></div> <div class="menu-item"></div> <div class="menu-item"></div> <div class="menu-item"></div> <div class="menu-item"></div> </div> gsap.to(".header-icons .menu-item", { opacity: 0, y: -80, stagger: 0.1, scrollTrigger: { trigger: ".full-row", start: 0, end: 230, scrub: 0.5, markers: true } }); Here is a fork of your demo: https://codepen.io/GreenSock/pen/oNOVqKm Hopefully this helps. Happy Tweening!
    1 point
  20. Hi @CRE-ATE welcome to the forum! What for me works best is having the scale always between 0 and 1, where 0 is the minimum and 1 is the max it can have, setting scale beyond that will result in rendering bugs/issues. So in your case make the logo as big as you want it to be and then scale it .to() the value you want it. See demo below. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/PogLKwP?editors=0010
    1 point
  21. In some cases, it's adequate to just call ScrollTrigger.sort() after all the ScrollTriggers are created which will set the refresh order according to the "start" value: https://codepen.io/GreenSock/pen/YzMgQrR?editors=1010 Is that what you're looking for?
    1 point
  22. Yeah, that definitely seems like a Firefox rendering bug, totally unrelated to GSAP. My guess is that the renderer needs time to create the raster image for each SVG image. It likely ignores them completely (skips creating the raster) initially because they are display: none. But once it flips to display: block once, that raster image gets created and cached by Firefox, thus it can show it faster next time. Maybe try setting ALL of the images to display: block initially, just for 1 tick maybe so that the browser gets them all rasterized/cached. Rodrigo's suggestion to go with canvas is very good too.
    1 point
  23. Unfortunately it's just not feasible to make ScrollToPlugin accommodate that automatically. There's no way it could understand which elements are affected by which ScrollTriggers. It's essential that you, as the builder of the page, provide that information. But here's a helper function you could try - it lets you create several lookups that get added together into one big lookup. Splitting them up like this allows you to segregate elements according to whether or not they're in a containerAnimation and/or pinnedContainer: function getScrollLookup(targets, {start, pinnedContainer, containerAnimation}) { targets = gsap.utils.toArray(targets); let initted, triggers = targets.map((el, i) => ScrollTrigger.create({ trigger: el, start: start || "top top", pinnedContainer: pinnedContainer, refreshPriority: -10, onRefresh(self) { if (initted && Math.abs(self.start) > 999999) { triggers[i].kill(); triggers[i] = ScrollTrigger.create({ trigger: el, start: start || "start start", pinnedContainer: pinnedContainer, refreshPriority: -10, }); } }, containerAnimation: containerAnimation })), st = containerAnimation && containerAnimation.scrollTrigger, lookups = [], lookup = target => { let t = gsap.utils.toArray(target)[0], i = targets.indexOf(t); if (i < 0) { for (i = 0; i < lookups.length; i++) { if (lookups[i].targets.indexOf(t) !== -1) { return lookups[i](t); } } return console.warn("target not found", target); } return triggers[i].vars.containerAnimation ? st.start + (triggers[i].start / containerAnimation.duration()) * (st.end - st.start) : triggers[i].start; }; lookup.targets = targets; lookup.add = (targets, config) => { lookups.push(getScrollLookup(targets, config)); return lookup; }; initted = true; return lookup; } So you'd use it like: let lookup = getScrollLookup("section.project", { containerAnimation: tween, pinnedContainer: el }); lookup.add("section.other", {}); // no containerAnimation or pinnedContainer lookup.add("section.pinned", {pinnedContainer: el}); // just a pinned container // then later... let position = lookup(".any-of-the-above-elements"); Hopefully that helps.
    1 point
  24. Hi, There are a few issues in your demo. First you're creating a single timeline for all the buttons, that is not going to work the way you intend, create the GSAP Tween/Timeline, inside the loop so each button controls it's own animation. Second, you have the same mask name for all the elements, which means that when one mask is animated it affects all the elements with that mask ID, create a different mask ID for each element. Here is a fork of your demo: https://codepen.io/GreenSock/pen/rNbRxrP Hopefully this helps. Happy Tweening!
    1 point
  25. Thanks I was stuck on doing it with gsap.from and this is simpler 😅 Sometimes you are so deep into something and you don't see that the solution is simpler
    1 point
  26. That seems a little more verbose than necessary: // LONG width: () => gsap.utils.random(0, 20, true)() + "%" // SHORTER width: () => gsap.utils.random(0, 20) + "%"
    1 point
  27. Well the only thing I can see is that there is no animation at all here: gsap.to(trigger, { scrollTrigger: { trigger: trigger, start: 'top center', y: '-=100px', ease: "power1.inOut", stagger: .2, onEnter: () => { console.log('coucou') } } }) You have a GSAP to() instance but there is no animation whatsoever, just the ScrollTrigger configuration, if you remove that this is what's left: gsap.to(trigger, { }) Nothing, nada, nicht. See the problem? You need to add some property for those elements that can be animated to begin with. Finally you're planning to animate the same element that you're using as a trigger on your ScrollTrigger configuration. Be super careful about that and if possible avoid animating the trigger element because it could lead to unexpected results. Happy Tweening!
    1 point
  28. Thanks, If others may need I m currently using import gsap from "gsap"; export class LifecycleManager { public ctx: gsap.Context | null; private boundAfterSwapHandler: () => void; private boundPageLoadHandlers: Map<string, () => void>; constructor() { this.ctx = null; this.boundAfterSwapHandler = this.onChangePage.bind(this); this.boundPageLoadHandlers = new Map(); document.addEventListener("astro:after-swap", this.boundAfterSwapHandler); } /** * Initialize the context */ initializeContext(): void { if (this.ctx === null) { this.ctx = gsap.context(() => {}); } } /** * Check if the component with the given ID exist in the DOM * @param id id of the element * @returns boolean */ elementExists(id: string): boolean { if (!id) throw new Error("ID cannot be null"); return document.getElementById(id) !== null; } /** * Execute the callback when the page is loaded and the component is visible * @param id id of the element * @param callback callback function */ onElementLoaded(id: string, callback: (ctx: gsap.Context | null) => void): void { if (!this.boundPageLoadHandlers.has(id)) { const handler = () => this.onPageLoad(id, callback); this.boundPageLoadHandlers.set(id, handler); document.addEventListener("astro:page-load", handler); } } /** * Callback for the page load event * @param id id of the element * @param callback */ onPageLoad(id: string, callback: (ctx: gsap.Context | null) => void): void { if (this.elementExists(id)) { this.initializeContext(); callback(this.ctx); } } /** * Revert the context */ revertContext() { if (this.ctx !== null) { this.ctx.revert(); this.ctx = null; } } /** * When changing page revert the context * and remove the event listeners */ onChangePage(): void { this.revertContext(); } /** * Cleanup all event listeners */ cleanup(): void { this.revertContext(); document.removeEventListener("astro:after-swap", this.boundAfterSwapHandler); this.boundPageLoadHandlers.forEach((handler, id) => { document.removeEventListener("astro:page-load", handler); }); this.boundPageLoadHandlers.clear(); } } export default LifecycleManager; usage <script> import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { SplitText } from "gsap/SplitText"; import { LifecycleManager } from "@/services"; gsap.registerPlugin(ScrollTrigger, SplitText); let manager = new LifecycleManager(); manager.executeWhenVisible("hero", () => { manager.ctx?.add(() => { const childSplit = new SplitText("[data-hero-text-reveal]", { type: "lines", linesClass: "split-child", }); const parentSplit = new SplitText("[data-hero-text-reveal]", { // type: "lines", linesClass: "split-parent", }); gsap.timeline() .set("[data-hero-text-reveal]", { opacity: 1 }) .from(childSplit.lines, { yPercent: 300, skewY: 7, stagger: 0.2, }) .to( "[data-hero-reveal]", { opacity: 1, stagger: 0.1, }, "<=", ); const pathsToAnimate = document.querySelectorAll( '[wb-element="path-to-animate"]', ); pathsToAnimate.forEach((path) => { const finalPath = path.getAttribute("wb-final-path"); gsap.timeline({ scrollTrigger: { trigger: path, start: "top bottom", end: "bottom top", scrub: 1, }, }).to(path, { attr: { d: finalPath || "" }, ease: "none", }); }); }); }); </script>
    1 point
  29. Example: CustomEase.create("in-out", "0.42,0,0.58,1") And then: gsap.to(".box", { ease: "in-out", ... });
    1 point
  30. Hi, In your original demo you had both toggleActions and scrub in your ScrollTrigger configuration. It has to be one or the other, but definitely not both, since they pretty much don't work together. From the ScrollTrigger docs: https://gsap.com/docs/v3/Plugins/ScrollTrigger/#config-object scrub Boolean | Number - Links the progress of the animation directly to the scrollbar so it acts like a scrubber. You can apply smoothing so that it takes a little time for the playhead to catch up with the scrollbar's position! It can be any of the following Boolean - scrub: true links the animation's progress directly to the ScrollTrigger's progress. Number - The amount of time (in seconds) that the playhead should take to "catch up", so scrub: 0.5 would cause the animation's playhead to take 0.5 seconds to catch up with the scrollbar's position. It's great for smoothing things out. toggleActions String - Determines how the linked animation is controlled at the 4 distinct toggle places - onEnter, onLeave, onEnterBack, and onLeaveBack, in that order. The default is play none none none. So toggleActions: "play pause resume reset" will play the animation when entering, pause it when leaving, resume it when entering again backwards, and reset (rewind back to the beginning) when scrolling all the way back past the beginning. You can use any of the following keywords for each action: "play", "pause", "resume", "reset", "restart", "complete", "reverse", and "none". I'd assume that Jack picked scrub over toggleActions, since your demo was scrubbing, but most definitely I can't read his mind! 😉 Hopefully this clear things up. Happy Twening!
    1 point
  31. Hi, Yeah this is not the easiest thing to achieve. Luckily @ceribbo was super kind to share a solution for a similar situation with the community in this thread: Hopefully it helps you and if it does, remember to thank @ceribbo for it. Happy Tweening!
    1 point
  32. Yeah that definitely makes it more clear to me Thanks a lot for your help and explanations!
    1 point
  33. But when the dependency is updated, the entire hook runs again and you have some lingering GSAP instances that are pointing to an element that is not rendered yet because of the useEffect/useLayoutEffect double call React does on Strict Mode. The useGSAP hook will revert only in the initial call of the hook but not when the dependencies are updated, why? Because we wanted to give users the opportunity to put all their logic in the useGSAP hook instead of creating a useGSAP and a useEffect hook, but not every time you need to re-run some logic because of a state/prop change you need to revert and re-create your GSAP instances, enter revertOnUpdate. With that useGSAP will not only run your logic inside the hook on a state/prop update but also revert and re-create your GSAP instances. Hopefully this clear things up. Happy Tweening!
    1 point
  34. Yes this is what I was looking for!!! I never knew about the immediateRender: false! Just one question: you removed toggleActions, but As I scroll back it it moves in reverse, why? Thanks for solving my problem!
    1 point
  35. Hi @vexkiddy welcome to the forum! Am I missing something? I don't see any GSAP code! Again we would love to see what you can come up with that way we can better help you understand the tools, so please try to get something working then someone here will definitely point you in the right direction. If you're new to GSAP check out this awesome getting started guide https://gsap.com/resources/get-started/ Hope it helps and happy tweening!
    1 point
  36. Hi, Jack is right, the issue is that in a Nuxt app you can access the runtime context with the useNuxtApp hook, but for using something from the app's context it has to be there first. In your demo I don't see any composable neither in the composables folder or in our package.json indicating that you installed a composable that abstracts GSAP's import statement: https://nuxt.com/docs/guide/going-further/nuxt-app https://nuxt.com/docs/guide/directory-structure/composables I think Jack's final suggestion is the best, just import GSAP in the files you need it and that should work. Happy Tweening!
    1 point
  37. Thank you so much @PointC for your prompt and helpful response! Your suggestion to use yoyo makes perfect sense. I really appreciate your guidance and warm welcome to the forum. I'll be sure to implement your advice and continue exploring the exciting world of GSAP. Thanks again for your support! Best regards,
    1 point
  38. Thanks, that's helpful. I went with the second suggestion and refactored my scroll effects to use the regular ScrollSmoother body transform. I was also able to use pinning to achieve the "pause" effect.
    1 point
  39. Hi @mukeshKumarSardiwal1997 and welcome to the GSAP Forums! Besides echoing the need for a minimal demo, there are a few things to note about your snippet. When using our useGSAP hook there is no need for manual cleanup of your GSAP/ScrollTrigger instances, the hook does that for you, so there is no need for this: useGSAP(() => { const tl = gsap.timeline.to(element, { scrollTrigger: {}, }); return () => { ScrollTrigger.revert(); }; }); Finally the onRefresh is a callback that works on the config object, but that can't be invoked directly like this: rwd.vars.scrollTrigger.onRefresh() You should be getting an error indicating that onRefresh is not a function. If you want to refresh a specific ScrollTrigger instance just do this: rwd.vars.scrollTrigger.refresh(); https://gsap.com/docs/v3/Plugins/ScrollTrigger/refresh() Hopefully this helps. Happy Tweening!
    1 point
  40. Aha! I forgot about the 'sticky' argument in this.update()
    1 point
  41. First of this is really important! ScrollTrigger can only be on the whole timeline or on a single gsap.to() (or .from()) tween. As soon as you define a timeline ScrollTrigger needs to control the whole timeline, it can't control individual tweens that are on a timeline, logically this is impossible. Next the best thing to do when working with ScrollTrigger is to remove it! This seems counter intuitive, but ScrollTrigger is just animating something on scroll, so just focus on the animation at first and only when you're happy with the animation add ScrollTrigger back in. This way you can focus on one part at a time and it will save a lot of headache when debugging. I’ve placed some comments in your JS to better explain things, please be sure to read through them! Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/MWRZego?editors=0010
    1 point
  42. Case solved Cause: Incorrect syntax. Change: ScrollTrigger.create({ target: ".services-content-heading-inner", start: "top top", end: "bottom bottom", markers: true }); To: gsap.fromTo('.services-content-heading-inner', { yPercent: 100 }, { yPercent: 0, autoAlpha: 1, duration: 2, ease: easeInOut, scrollTrigger: { trigger: '.services-content-heading-inner', start: "top top", end: "bottom bottom", markers: true } } ); Thank you!
    1 point
  43. I noticed several problems: You had faulty markup (missing closed </div> tags) You were defining a containerAnimation for the getScrollLookup(), but only some of the elements were in the containerAnimation. That is not valid. You were pinning a container element, but you didn't define that in the getScrollLookup() config. Is this more of what you wanted? https://codepen.io/GreenSock/pen/RwOEWYW?editors=0010
    1 point
  44. We have several mouse follow effect threads. Here are a couple that should point you in the right direction. Happy tweening.
    1 point
  45. Thanks for the demo. In the future please add new information to new replies. Editing the first post repeatedly makes it difficult for us and others to follow the conversation. I don't know enough about swiper in react to help you with this, particularly how to reference the Swiper inside the onChange callback. However, this bare-bones example shows how to animate the active slide (change it's scale) and animate something inside it (rotate the number div) Hopefully you can find a way to apply something similar to your project https://codepen.io/snorkltv/pen/WNmzezX?editors=0011
    1 point
  46. Very sorry about that, @stijlmassi and @grizhlie, but Lenis is a completely different type of solution. Both ScrollSmoother and Lenis have pros and cons to their approaches. Our original goal was to leverage native scroll technology (no scrolljacking, just let the user drag the scrollbar wherever they want but have the content smoothly "catch up"). This means that EVERY way of scrolling gets smoothed (drag the scrollbar, press the arrow or spacebar keys, etc.) whereas I think Lenis is more about intercepting mouse wheel events, preventing them, and basically creating a tween of sorts that updates the scrollTop/scrollLeft value. But it won't smooth if you scroll in other ways, as mentioned earlier. And of course ScrollSmoother has a bunch of extra features like lag and speed effects, etc. I'm not criticizing Lenis at all - if it suits your needs, fantastic. Use it. Works great with GSAP/ScrollTrigger. But the fundamental nature of HOW things get scrolled is completely and radically different in ScrollSmoother. It's simply not possible, as far as I know, to completely solve the issues you mentioned while at the same time smoothing all of those inputs across the board and avoid scrolljacking. In a future major release, we very well may opt for a totally different approach but for now it's not really feasible (as far as I know) to "fix" what you mentioned because Safari is just absolutely terrible with various scroll-related bugs. We've spent many hundreds of hours trying to come up with the silver bullet workaround and the response we got from the Safari team was basically "nope, it's impossible". We'll definitely continue to look for ways to improve things in the coming months. If anyone has specific suggestions, we're all ears.
    1 point
  47. Hi @PapaDeBeau. Are you talking about with ScrollSmoother in particular? Or a non-ScrollSmoother-controlled page? It'd definitely increase your chances of getting a solid answer quickly if you provide a super minimal demo showing just a basic idea of what you want to do (even if it's not functional). And are you trying to navigate to an anchor onclick or when the page loads? ScrollSmoother has a very easy way to animate to a particular element: https://greensock.com/docs/v3/Plugins/ScrollSmoother/scrollTo() And if want to do that onload, @Cassie already had that in her demo. You could do: window.addEventListener("load", () => { let urlHash = window.location.href.split("#")[1], scrollElem = document.querySelector("#" + urlHash); if (scrollElem) { // if you've got a ScrollSmoother instance... smoother.scrollTo(scrollElem); // otherwise... gsap.to(window, { scrollTo: scrollElem }); // don't forget to load/register ScrollToPlugin } }); Does that help?
    1 point
  48. I couldn't help myself - I imagine this may assist others looking to do something similar so I went ahead and created this solution: https://codepen.io/GreenSock/pen/zYRmbEM?editors=0010 The basic concept is to create a simple ScrollTrigger for each box and then use the "start" of each of those to calculate the 0-1-based progress value for snapping on the overall section ScrollTrigger. Does that help?
    1 point
  49. @OSUblake, thank you for your response. At the moment, I set for the trigger end '3000px', but I would like to have this value dynamical because my users will have different number of items there. So I need to calculate the height of the trigger dynamically. Hopefully I express it clearly. Would it be possible to use something like this to get the height of all images (or something else with a clear height) there and use this value as trigger end? let contents = document.querySelectorAll('.image-container') let contentHeight = gsap.getProperty(contents, "height"); Here my .js gsap.registerPlugin(ScrollTrigger); let tl = gsap.timeline({ scrollTrigger: { trigger: ".layer-story-pinned-content", scrub: true, pin: true, start: "top 30", end: '3000px', //endTrigger: '.layer-story-pinned-content', markers: true } }); tl.from( ".layer-story-content-wrapper", { y: 20, autoAlpha: 0, stagger: 0.5 }, 0 ).to( ".layer-story-content-wrapper", { y: 0, autoAlpha: 0, stagger: 0.5 }, 0.4 ); tl.from( ".layer-story-image", { y: 0, autoAlpha: 0, stagger: 0.5 }, 0 ).to( ".layer-story-image", { y: 0, autoAlpha: 1, stagger: 0.5 }, 0.4 ); https://codepen.io/jankout/pen/ZEJOypz
    1 point
  50. If I'm not mistaken @Dipscom has some experience with Svelte in production, chances are that He has used it with GSAP as well.
    1 point
×
×
  • Create New...