Jump to content
GreenSock

Rodrigo last won the day on May 2 2021

Rodrigo had the most liked content!

Rodrigo

Moderators
  • Posts

    2,114
  • Joined

  • Last visited

  • Days Won

    168

Everything posted by Rodrigo

  1. Hi, You could try using a state property that should be updated after the smooth scroll instance is created, then in a different useEffect hook, create the ScrollTrigger instance after that state property is updated: const [scrollBarCreated, setScrollBarCreated] = useState(false); // This could also be a use callback as Ryan suggests // but use effect should work useEffect(() => { Scrollbar.init(); setScrollBarCreated(true); // remember to cleanup your instances here return () => {}; }, []); useEffect(() => { if(scrollBarCreated) { ScrollTrigger.scrollerProxy(".scroller", { scrollTop(value) { if (arguments.length) { bodyScrollBar.scrollTop = value; } return bodyScrollBar.scrollTop; } }); } // remember to cleanup your instances here return () => {}; }, [scrollBarCreated]); Another alternative would be to create a setTimeout and wait for the smooth scrollbar to be completely created before creating the scroll trigger instance. I know is far from being the react-way, but that would definitely pinpoint the issue to the DOM not being ready when the GSAP code is executed. If that does work, then you'll need some way do that check in a more react-ish way. This was discussed in this thread some time ago: Another alternative is too use useLayoutEffect and see in there how things are changing and if at any stage during those changes, it's safe to create the scroll trigger instance. Happy Tweening!!!
  2. Hi, On top of Blake's great advice, perhaps the solution in this thread could help you get started: Happy Tweening!!!
  3. Definitely!!!! Also I strongly recommend to never update a component's state on every event handler call such as window resize, mouse move or a GSAP update, unless you have no other choice. That will trigger a re-render on every state update (in the case of GSAP that could happen up to 60 times per second), and also re-render all the children in that component's tree. If possible use a breakpoint like in the article, but run your code only if the breakpoint is passed, like that you will avoid a bunch on unnecessary calls. Something like this: const breakPoint = useRef(768); const [width, setWidth] = useState(); const [isSmall, setIsSmall] = useState(true); const windowResizeHandler = () => { const {innerWidth} = window; if (innerWidth < 768 && !isSmall) { setWidth(window.innerWidth); setIsSmall(false); } if (innerWidth > 767 && isSmall) { setWidth(window.innerWidth); setIsSmall(true); } }; useEffect(() => { window.addEventListener("resize", windowResizeHandler); return () => window.removeEventListener("resize", windowResizeHandler); }, []); useEffect(() => { // THIS CODE RUNS ONLY IF THE BREAKPOINT IS PASSED return () => { // Remember to cleanb up before unmounting the component }; }, [isSmall]); Also if you have no other option but to update the state on every resize event, keep your code as simple as possible and try wrapping any direct child with a React.memo() method in order to prevent unnecessary re-renders. Finally use the profiler you can find in react dev-tools in order to find out about those re-renders: https://www.youtube.com/watch?v=00RoZflFE34 https://www.youtube.com/watch?v=o-alRbk_zP0 Happy Tweening!!!
  4. I can see the use of this in React mostly when dealing with a lot of refs. In Vue it would also be useful but only to reduce template boilerplate, that is defining the ref name in the template tag. Vue is smart enough to handle all refs inside an object, which makes it super easy to access, also if you use a ref in a v-for loop, each DOM node will reside in an array with the name of such ref. So Vue kind of makes things easy for developers that way, but that approach is not very well known. In React you have to define the ref in the code and then add it in the JSX which leads to a lot of refs in larger components that can't be break down into smaller ones. Of course you can go the Vue route and create a single object for your refs and store them in it using the ref callback, which is something React should have thought about, but yet again, there are many things the React team should've thought about 🤷‍♂️. Personally I do that when there are more than 3-4 refs in a component. It's far simpler and easy to work with and you define kind of a store for refs soto speak. To summarize: There are simple and easy ways to handle a bunch of refs in a component that most users are either unaware of or too lazy to implement, so in those cases it would help. Finally in the particular case of this thread it would also help of course.
  5. Hi, This should be the easiest/simplest setup for the codepen @Cassie linked in a React app: https://codesandbox.io/s/lively-sunset-9ifwq?file=/src/App.js Happy Tweening!!!
  6. As Jack mentions the culprit here is layout shifting, something I really don't enjoy as a user, so the trick of setting heights to your elements is a good way to avoid that. Another alternative could be to use the useLayoutEffect hook and see of that helps: https://reactjs.org/docs/hooks-reference.html#uselayouteffect Happy Tweening!!!
  7. Hi, Delay shouldn't be a string, it should be a number. Also the reason why the position parameter is not working is kind of a mystery, based on this it should work: https://github.com/pankod/canvas2video/blob/master/src/renderer.ts#L42 That is a timeline instance, perhaps something in that package is not working properly. I'd recommend you to try the delay with a number and create an issue in order to get the position parameter to work in the canvas2video repo. Happy Tweening!!!
  8. Hi, In terms of performance, both are equal. None of the options is going to put a lot of stress in either the CPU or the GPU. In terms of cleanliness, I'd prefer the first one, because if your GSAP instance is not going to change at any point in time, there is no need to create it over and over again, probably generating some overwrite and initial position problems. I always prefer to store my GSAP instance and use them as needed and only rely on creating GSAP instance on every event handler call when I have no other choice. Happy Tweening!!!
  9. Not really, while you ca create some defaults those actually doesn't work in the way you intend. What you can do is create a factory function that creates and returns a GSAP instance: const createTween = (target) => { return gsap.timeline({...}) .to(target, {...}); }; Happy Tweening!!!
  10. Hi, It seems that this could be doing what you're trying to do: https://codepen.io/rhernando/pen/WNpevbv Just take advantage of the new syntax, also no need to use a callback to automatically reverse the timeline, just use repeat: 1, yoyo and repeat delay in order to create that effect. Finally if you want to pass the element to the specific callback by using the params property that each callback has: // I recomend that for a specific element use an ID attribute instead of a class selector const btn = document.getElementById("btn"); const myCallback = (element) => { console.log(element); // -> btn }; gsap.to(btn, { x: 100, onComplete: myCallback, onCompleteParams: [btn] }) Happy Tweening!!!
  11. As Blake points, you can. Vue is smart enough, so when you create a reference in the vue instance you can reach it anywhere in your code using the this keyword. For example you can create a method and call that method anywhere in your code using this.myMethod(), but myMethod is not in the data object. Same thing with the mounted method, any property you attach to this will be reachable anywhere where this points to the Vue instance. Agree, please give it a try
  12. In your Start.vue file: data() { return { menuItems: [...], menuTween: null, // this is replaced by the GSAP instance }; }, After this, everytime you do either of these: this.menuTween = gsap.to(); vm.menuTween = gsap.to(); The reactive property is updated and triggers an unnecessary render of the component, because nothing in the component actually depends on that particular property being updated. Happy Tweening!!!
  13. Hi, What I meant is that is not entirely recommended adding a GSAP instance to the object returned by the data method. Mostly because GSAP shouldn't have any effects on the component's reactivity. Is better to simply attach it to the Vue instance using either this.menuTween or vm.menuTween. You can still access the property using the this keyword anywhere in your app. Basically your data method should look like this (based on the codesandbox sample): data() { return { menuItems: [ /* Array Elements */ ], }; }, But, as I mentioned above, you can still attach the GSAP instance to the component: beforeRouteEnter(to, from, next) { next((vm) => { // Assign to var in order to be able to kill it later vm.menuTween = gsap.to(/*...*/); }); }, // Or using mounted mounted() { this.menuTween = gsap.to(/*...*/); } You can read more about it here: https://vuejs.org/v2/guide/instance.html#Data-and-Methods I also recommend you to take a look at this resources, in order to learn more about Vue. Brad Traversy has a lot of free stuff in many subjects and has a couple of new Vue courses here: https://www.youtube.com/c/TraversyMedia/videos Also Erik Hanchett is a big Vue evangelist and has a ton of Vue stuff: https://www.youtube.com/c/ProgramWithErik/videos Happy Tweening!!!
  14. Hi, Well your setup is quite unusual I must say, but the main issue lies in this lines: resetNav(next) { this.menuTween.kill(); gsap.killTweensOf(this.menuTween); // PROBLEM HERE gsap.set(this.menuTween, { clearProps: "all" }); // PROBLEM HERE next(); }, beforeRouteEnter(to, from, next) { next((vm) => { // Assign to var in order to be able to kill it later vm.menuTween = gsap.to( menuItems, {/**/}, 0.65 // PROBLEM HERE ); }); }, In the resetNav method, you're use killTweensOf and then you're trying to clear the applied styles using clearProps on a GSAP instance. The killTweensOf() method works on the DOM node or object GSAP is actually tweening, in this case a list of DOM nodes. You should pass the collection of the nav elements to this method in the onComplete callback. Then in the beforeRouteEnter hook you're passing an extra parameter to a GSAP instance that is not a timeline, so basically there is no use for that there. Just remove that. This code seems to do what you're after: methods: { resetNav(next, menuItems) { this.menuTween.kill(); gsap.set(menuItems, { clearProps: "all" }); next(); }, }, beforeRouteEnter(to, from, next) { next((vm) => { vm.$forceUpdate(); const menu = vm.$refs.menu; const menuItems = menu.getElementsByTagName("li"); gsap.set(menuItems, { autoAlpha: 0, y: -32, x: -48 }); // Assign to var in order to be able to kill it later vm.menuTween = gsap.to( menuItems, { autoAlpha: 1, duration: 1.5, ease: "elastic", stagger: 0.09, x: 0, y: 0, force3D: true, onComplete: next, } ); }); }, beforeRouteLeave(to, from, next) { const menu = this.$refs.menu; const menuItems = menu.getElementsByTagName("li"); this.menuTween.kill(); gsap.to(menuItems, { autoAlpha: 0, duration: 0.4, ease: "power2.in", stagger: -0.1, x: 32, y: 16, force3D: true, onComplete: this.resetNav, onCompleteParams: [next, menuItems], }); }, Finally there is no need to add the GSAP instance as a reactive property in the Vue instance, you can attach it using this.menuTween or vm.menuTween Happy Tweening!!!
  15. Hi, You mean a different start and end points depending on the scroll direction? If that's what you're looking for, I don't know if that's possible with the current API. It seems that the config() method is intended for other purposes, as start and end values are read only. The only thing I can think of is to re-create the ScrollTrigger instance with the new values using the onLeave and onLeaveBack callbacks. Perhaps @GreenSock or @OSUblake could shed some light on that subject. Happy Tweening!!!
  16. Hi, Basically they're using this background: And they're using PIXI's tiling sprite class: https://pixijs.io/examples/#/sprite/tiling-sprite.js As for the deformation, it seems that they're using the displacement map filter: https://pixijs.io/examples/#/filters-basic/displacement-map-flag.js With this image: Perhaps @OSUblake our resident PIXI wizard can see something else in it and offer some advice. Chances are that perhaps He has a codepen in his vast collection that is similar to this. But if you're not quite familiar with PIXI and how it works, Mikel's morph SVG solution should be a very good starting point. Happy Tweening!!!
  17. Hi, If you're looking to tween the line from right to left, using this in your path's d attribute does the trick: <path id="first" style="stroke-width:1;fill:none;stroke:#000;" d="M77,8 L0,8"></path> If you're looking for a different shape, honestly I don't know, SVG is not my strongest suit. It seems that you already took a look at this thread: And to this article by @PointC: https://www.motiontricks.com/adobe-illustrator-path-direction-quick-tip/ Perhaps @PointC, @Cassie or @OSUblake (didn't forget this time ) our SVG wizards could enlighten us in a different way to do this. Happy Tweening!!!
  18. DOH!!!! Just ignorance on my end my fried. Although in my list you're our Canvas/PIXI/WebGL wizard and also on other categories as well
  19. Hi, The value passed to the modifiers plugin is actually a string and has px at the end, so basically the method is returning NaN. This seems to work: function animateCarousel(delta) { gsap.to(boxes, { duration: boxes.length * 0.5, x: function(i, target) { var x = Math.round(gsap.getProperty(target, "x")) + wrapWidth * delta; return x; }, ease: "none", modifiers: { x: function(x) { return wrap(parseFloat(x), -boxWidth, wrapWidth) } }, repeat: -1 }); } function wrap(value, min, max) { var v = value - min; var r = max - min; return ((r + v % r) % r) + min + "px"; } Happy Tweening!!!
  20. Hi, The set() instance you're creating is actually active, so as soon as the browser finish going through the code, the circle's radius attribute is updated. That instance has to be paused: const anim = gsap.set("#circle2", { attr: { r: 330 }, paused: true, immediateRender: false }); Happy Tweening!!!
  21. Hi and welcome to the GreenSock forums. It seems to me that the Tesla site is not using the same approach of the codepen. Basically the site checks different events (mouse wheel, keyboard, etc) and scrolls the entire viewport to a specific section of the page. Also if you move scroll down mid-section using the scroll bar and then use an event, it also behaves a bit weird. Honestly, if I was using that approach I'd remove the scroll bar and follow the code used in this samples by @PointC: https://codepen.io/PointC/pen/MzWzyv https://codepen.io/PointC/pen/YRzRyM Happy Tweening!!!
  22. Hi, I honestly don't know how to achieve that with SVG, perhaps our svg wizards @Cassie and @PointC could share some wisdom on the subject. I know that this could be achieved with PIXI and the displacement filter, using WebGL: https://codepen.io/rhernando/pen/94c6a32a3faa30808dc507b1fa809d74?editors=0010 This is the image the displacement map uses to create the effect: https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/ripple-03.png https://en.wikipedia.org/wiki/Displacement_mapping Happy Tweening!!!
  23. Hi, In the first codepen each number it's inside a different <svg> tag, therefore when you loop through each SVG container, you're basically looping through each number at a time. Based on your code you should add all three numbers in the same <svg> tag and add the data-stagger attribute to them. Another option is to give those particular svg elements a unique class in order to loop through them independently from the rest and add a stagger based on the index value of the current iteration. Finally, as I understand that it might take longer and not be the most elegant solution, in cases like this I prefer to create my animations for each section in their own code block. Sure, creating a single loop for everything is simpler and shorter, but as you already found out, it comes with a few drawbacks. Since you have some similar sections (the wavy line) you can loop through those, but for the rest my advice is to create an independent code block for each. Happy Tweening!!!
  24. Hi, Can you provide a reduced live editable sample in codesandbox? With the information you provide is a bit hard to pinpoint what the problem could be. The only guess I can take is that you're adding more elements dynamically and that is messing up the current set up. For that you'll have to create a separate useEffect hook where you create the initial setup and the endless animation when the amount of text elements change. Happy Tweening!!!
×