Jump to content
Search Community

Rodrigo last won the day on April 28

Rodrigo had the most liked content!

Rodrigo

Administrators
  • Posts

    6,643
  • Joined

  • Last visited

  • Days Won

    287

Everything posted by Rodrigo

  1. Hi, Yeah most likely the update callback would involve looping through all the elements. Another option could be to create an array of dummy Objects, animate the properties of those using advanced staggers and using the object's set method to update the corresponding THREE cube. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set Something like this: https://codepen.io/GreenSock/pen/dywqpJZ Just loop through the cubes, create an object for each and point each cube when updating that particular object. Hopefully this helps Happy Tweening!
  2. Hi, As the spec says I wouldn't use zoom in any production site if I was you. That being said the MDN page states: That means that the entire layout of the document is being affected which changes the height of the document once the animation is completed. In this demo you can see the lower spacer section being pushed down because of that: https://codepen.io/GreenSock/pen/PoXdNXZ So every ScrollTrigger instance created after that will be affected since those calculations will have an offset created by the zoom level and the height of the document being updated because of it. What you could do is use an onLeave callback in order to call ScrollTrigger.refresh() only after the first run: let firstRun = true; gsap.to(".box", { zoom: 1.5, scrollTrigger: { trigger: ".box", start: "top center", end: "bottom center", scrub: true, onLeave: () => { if(firstRun) { firstRun = false; ScrollTrigger.refresh(); } }, markers: true, }, }); Hopefully this helps. Happy Tweening!
  3. Hi @Robert Wildling, is the subject of your post in this thread maybe related to this one?: Let us know so we can move this posts over there. Is better to keep this thread about it's original subject about creating a Codepen demo. Happy Tweening!
  4. Hi @TruongNH, You already created a thread involving the exact same code here: In fact your codesandbox link is the same and you haven't followed the advice I gave you in the other thread about using GSAP Context and simplifying your setup. Once again, as I mentioned in the other thread, we don't have the time resources to provide general consulting in these free forums. In the other thread I provided a link to our GSAP & React page with a couple of articles and several examples that should help you. Finally I'd strongly recommend you (again) to start simple and then add more complexity to your app. Good luck with your project! Happy Tweening!
  5. Hi, If you want to create your own implementation (although in production I'd strongly recommend using the Helper Function) you should use the Modifiers Plugin: https://greensock.com/docs/v3/GSAP/CorePlugins/ModifiersPlugin Here is an example: https://codepen.io/GreenSock/pen/QEdpLe Hopefully this helps Happy Tweening!
  6. Hi, Maybe this examples can help understanding how to use Flip with ScrollTrigger when scrolling in both directions: https://codepen.io/GreenSock/pen/YzZyQqv https://codepen.io/GreenSock/pen/bGxOjeP Hopefully this helps. Happy Tweening!
  7. Hi, A few things to notice. I'm not super experienced with Chrome Task Manager (I used it mostly to kill a tab that gets stuck) so IDK how reliable it is. GSAP is actually doing what is supposed to do, if you inspect the DOM elements being animated, you'll see that there are 3D transforms applied to them and you'll see the transform matrix being updated as the animation progresses. That creates a new GPU Layer which improves performance. Finally if you check the performance tab in dev tools you will see the CPU being used after the click event is triggered: In the image you can see the pointer event (click) then below that a bunch of colored lines, those indicate all the JS methods being called. Below that you'll see green lines, those are the moments when the GPU chips in because GSAP is using 3D transforms to create the animation. As you can see the GPU is actually kicking in when is required but, most likely, since your animations are quite simple the processing time required is really small (as shown in the image) so maybe the Chrome task manager doesn't register, perhaps due to the refresh rate it has. Happy Tweening!
  8. Hi, Your example is a quite convoluted IMHO. Again I don't see the benefit of that ScrollManager.js file that you are using like a component. If you want to use that approach I would suggest a custom hook, but not a component. You're still not using GSAP Context, as suggested in the articles you can read in the link I provided in my previous post. Unfortunately we don't have te time resources to create custom solutions for our users, solve complex logic issues or comb through large codebases trying to find errors and or issues, so I can't create a port to React of this example at the moment. The best I can do is create a Codepen example fo multiple Observer sections: https://codepen.io/GreenSock/pen/zYyLdrB The JS logic is not out of this world and you should be able to port it to React by following our guide to use GSAP in React environments. I think your best alternative is to create something as simple as possible and start adding enhancements to it as you go along. Hopefully this helps. Happy Tweening!
  9. The issue is that the specific component you mention never gets unmounted, unless the layout component gets unmounted. This is the returned HTML from your layout component: <html lang="en"> <body className={inter.className}> <main className="flex min-h-screen flex-col items-center justify-between p-24 pb-0"> <PinSections>{children}</PinSections> </main> <footer className="flex flex-col px-24 pb-5 border border-sky-500"> <p> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Tempora soluta culpa at numquam quae asperiores maxime quia porro nihil temporibus minus, nisi corrupti voluptate adipisci ad quisquam qui quidem obcaecati. </p> </footer> </body> </html> The children prop wrapped in the <PinSections> is the current page being rendered, so the cleanup phase in the effect hooks in those files will run, but <PinSections> is mounted when the app first mounts. Sure it will re-render but never will unmount so the GSAP Context revert method inside of it will never be executed. Once again this stems from creating what IMHO is an extremely convoluted setup that is not really necessary, as far as I can see. The issue is an arquitectural decision more than a faulty GSAP implementation. If you want to apply a similar ScrollTrigger config to some sections of several pages, it'd be better to just create a custom hook or a function that returns the GSAP Context instance in order to use that in the pages and not in that particular component. If you want to keep that particular component, then wrap the content of the pages around it and not the children prop passed to the layout component. Hopefully this helps. Happy Tweening!
  10. Hi, The main issues is that you are wrapping your GSAP MatchMedia instance with a GSAP Context instance and your breakpoints. There is no need for using GSAP Context when you use GSAP MatchMedia. MatchMedia is a Context instance with responsive super powers, so you can revert that particular instance and everything should work in the same way. You can also add a scope in the add() method or you can use a scope in a toArray() utility method like this: onMounted(() => { const boxes = gsap.utils.toArray('.box', main.value);// <- Scope for the selector mm.add('(min-width: 768px)', () => { tl = gsap .timeline() .to(boxes[0], { color: 'red' }) .to(boxes[1], { x: 300 }, '<') .to(boxes[2], { y: 300 }) .reverse(); }); mm.add('(max-width: 767px)', () => { tl = gsap .timeline() .to(boxes[0], { x: 120, rotation: 360 }) .to(boxes[1], { x: -120, rotation: -360 }, '<') .to(boxes[2], { y: -200 }) .reverse(); }); }); Since in this case the elements with that class are the same in every screen size there is no need to add that in the add() method. If the elements do change based on the screen size, then you can pass a scope to the add() method: let mm = gsap.matchMedia(); mm.add("(min-width: 768px)", () => { // ... }, main.value); // <- Scope! Then these breakpoints are not going to work as you expect for a couple of pixels (299 and 300): mm.add('(min-width: 299px)', () => { }); mm.add('(max-width: 300px)', () => { }); Those will overlap because you're telling GSAP do this when the size of the screen is more than 299 pixels, and do that until the width of the screen is between 0 and 300 pixels. The first one will run while the first one is still active. When you reach 300 pixels width both are active and overlapping. https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries https://medium.com/@banuriwickramarathna/min-width-max-width-media-queries-994e2ec5fca3 Here is a fork of your example: https://stackblitz.com/edit/nuxt-starter-cuqke7?file=pages%2Findex.vue Hopefully this helps. Happy Tweening!
  11. Hi, I think is better to use ScrollTrigger to get the new elements instead of using the intersection observer: https://codepen.io/GreenSock/pen/YzOzjbL The idea is basically to attach a ScrollTrigger instance to the body tag and then when you're close to the end of the document bring in the new elements in order to add them to the DOM. There might not be a need for the ScrollTrigger Batch approach in your case or maybe there is, you'd be the judge of that. Hopefully this helps. Happy Tweening!
  12. Potaito/potato, pattern/anti-pattern... Our mantra around here is: test, test, test. If leaving the modal on the DOM doesn't hurt performance, creates a memory leak, causes a breaking error, etc. Then just leave it there. As long as the content of the modal is simple and small enough that doesn't cause your DOM Tree look insane, then leave it there. Of course a React purist will tell you that you are committing a capital sin, that you will be banished for life from using React and your avatar everywhere will have some letter in a very striking color so everyone can shame you. I can't think of a reason why a normal modal will have such effect, also it seems that you want to render a menu element right? So the content will always be the same and it won't change, there is nothing wrong IMHO with leaving that there all the time. Just test and see how it works. @mvaneijgen always says: If it works, it works. Those are wise words, believe me. Happy Tweening!
  13. Hi, I'm having a hard time following your example. Can you please simplify it as much as possible? Also you have a warning about the content element passed to ScrollSmoother not being valid also is better to register your plugins inside a useEffect hook as well. We have a ScrollSmoother template for Next apps using the app router: https://stackblitz.com/edit/nextjs-ysf649 As you can see our approach to creating the ScrollSmoother instance is quite simple. I understand that all that fancy abstraction looks good, but I think is better to use that pattern when it actually becomes useful and completely necessary, otherwise it makes following what is going on in your code more dificult. Hopefully this helps. Happy Tweening!
  14. I forgot, this is the debug URL so you can check the result without any iframes and see how it works after a reload: https://cdpn.io/pen/debug/oNJMgYp Happy Tweening!
  15. Hi, I think you are over-engineering this waaay too much IMHO, which ultimately is causing the issue you're currently having. Here is a simpler approach using a single timeline: https://codepen.io/GreenSock/pen/oNJMgYp The CSS is not the same, but the idea is to give you an alternative that doesn't cause the same behaviour your example does. Hopefully this helps. Happy Tweening!
  16. Hi, I don't have a lot of experience with THREE so I can't do a lot to replicate your issue. Most likely this seems related to the fact that you're passing an object to GSAP in a way it can't be handled. You need to target the x property of the scale object. One option I can think of is to create an array of objects that you can tween and use the onUpdate method to apply the changes to each cube element, but that would imply to loop through all the cubes in the callback in order to apply the scale values to each cube: const scales = gltf.scene.children.map(cube => { cube.castShadow = true; cube.receiveShadow = true; cube.scale.set(0.2, 0.2, 0.2); return { x: 0.2 }; }); Then you can run GSAP with advanced staggers on that array: gsap.to( scales, { duration: 2, x: 1, stagger: { amount: 0.1, grid: "auto", from: "center" }, onUpdate: updateScales, }); Sorry I can't be of more assistance, but this is not really a GSAP issue but a limitation of sorts in the way THREE (and most of the WebGL libraries I know for that matter) works when it comes to it's display objects/geometries. Hopefully this helps. Happy Tweening!
  17. Hi, The main issue is that you are running the conditional rendering logic before the animation happens. Here is a quick and simple way to demonstrate this flow. You pass the state property that renders the element, the element is rendered, then the animation is created and finally the IN animation runs. So far no problem. Then you switch the state property and the animation is created or it could be reversed, but a few milliseconds later, since you are conditionally rendering the element, it is removed from the DOM without the animation never being completed. You need two different state properties. One outside the component you want to conditionally render and animate and another inside of that component. The one outside the component should tell the component to render it's content and to start the OUT animation. The one inside the component should tell the component to start the IN animation and to not render the component anymore. It might sound complicated but is just basic React logic, or as a matter of fact any other of these frameworks if you want to do this manually. Is far simpler to use a third party component that handles all that inner logic for you. In the case of React React Transition Group. Here is a simple demo of using it for a modal element: https://stackblitz.com/edit/vitejs-vite-vlpbvk?file=src%2FApp.jsx,src%2FModal.jsx&terminal=dev As you can see the setup is far simpler and you don't have to worry with an extra state property inside or outside the component. Hopefully this helps. Happy Tweening!
  18. Hi @AllenIVe, I forked Blake's codepen, is just about some CSS: https://codepen.io/GreenSock/pen/RwEJzVV Hopefully this helps. Happy Tweening!
  19. Hi, I think the issue lies in the fact that you are removing an event listener on a different add() method in your MatchMedia instance: mm.add("(max-width: 767px)", () => { // This is defined inside this scope // Not available outside this add() method's callback const handleBurgerMenuClick = () => { ... }; burgerMenu.addEventListener("click", handleBurgerMenuClick); }); mm.add("(min-width: 768px)", () => { // Removing event listener // but handleBurgerMenuClick is not defined inside this scope burgerMenu.removeEventListener("click", handleBurgerMenuClick); }); GSAP MatchMedia has a cleanup function that is returned in the add method that allows you to do that: mm.add("(max-width: 767px)", () => { return () => { // optionally return a cleanup function that will be called when // none of the conditions match anymore (after having matched) } }); So in your case it would be like this: mm.add("(max-width: 767px)", () => { // This is defined inside this scope // Not available outside this add() method's callback const handleBurgerMenuClick = () => { ... }; burgerMenu.addEventListener("click", handleBurgerMenuClick); return () => burgerMenu.removeEventListener("click", handleBurgerMenuClick); }); mm.add("(min-width: 768px)", () => { // Don't care about that event listener here }); That seems to work in the way you intend: https://codepen.io/GreenSock/pen/GRPGaPj Give that a try and let us know how it works. Hopefully this helps. Happy Tweening!
  20. Rodrigo

    GSAP

    Hi @hexciaLondon and welcome to the GreenSock forums! I made some changes to your CSS and your code: https://codepen.io/GreenSock/pen/bGOKJjN Basically there were two issues. The width of the gallery container wasn't spreading based on the width of the boxes and you were passing a string value to GSAP: tl.to(gallery, { x: `-${gallery.offsetWidth}`, // This is a string scrollTrigger: { trigger: galleryWrapper, start: 'top top', end: `+=${gallery.offsetWidth}`, pin: true, scrub: 0.5, } }) There is no need to pass a string, although it won't cause a major issue though, but the width of the boxes container was basically the major problem. Hopefully this helps. Happy Tweening!
  21. Hi, Maybe the threads @alig01 shared in this post could help: Happy Tweening!
  22. Hi, Indeed ScrollSmoother has a speed config option that's allows you to do exactly what you're looking for. From the ScrollSmoother docs: speed Number - a multiplier for overall scroll speed, so 2 would make it scroll twice the normal speed, and 0.5 would make it scroll at half-speed. added in version 3.11.4. Hopefully this helps. Happy Tweening!
  23. Hi, Just use a click event handler that updates the state in order to play/reverse the timeline on an useEffect hook with that state as a dependency. The rest is pretty much all in there since the onComplete and onReverseComplete callbacks will do the rest. This basically a simple react question and not a GSAP one. I'd recommend you to check react's docs in order to get a better grasp of it and check the resources in our GSAP and React page, that I linked on my first post. Happy Tweening!
  24. Hi, This is a bit complex and mostly implies the starting and end points of each ScrollTrigger instance for each section, plus the start and end points of the instance that you want to scrub. That needs some extra space before the next section switch happens. Here is my initial approach to this: https://codepen.io/GreenSock/pen/QWzrRxm Hopefully this helps. Happy Tweening!
  25. Hi, The simplest way is to add a label at the start of the timeline, before every other instance: const tl = gsap.timeline({ defaults: { paused: false, ease: "none", duration: 2 }, scrollTrigger: {...} }); tl.add("start"); That adds an extra stop at the start of the timeline and probably would make all the margins and other stuff to offset everything 10 pixels down not needed, but you'd have to test and see. Here is a fork of your codepen: https://codepen.io/GreenSock/pen/GRPdaoo Other than that it would require to write all the logic for the snapTo on a function in order to check the direction of the ScrollTrigger instance, current progress and check where the ScrollTrigger instance should land. Hopefully this helps. Happy Tweening!
×
×
  • Create New...