Jump to content

Rodrigo last won the day on May 2 2021

Rodrigo had the most liked content!


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Rodrigo

  1. Hi, I don't have a lot of time to go through all your code but there are a few things that caught my attention. First your set up is a bit convoluted IMHO, but that's more of a personal opinion. Try to break things into small presentational components, for example no need to have all the markup for the buttons in that component. Also the way you're using the classes is a bit outside my comfort zone, again just my two cents on the subject. Regarding the code, at some point the content of your app will be dynamic or it will always be static? If your app will be static, then you can use regular selectors with querySelectorAll otherwise you should use refs. Finally the problem is because at some point when the click events happen too fast the state is updated after a second consecutive click, so at that point the value of currentSlide is bigger than 2, which returns undefined because the array's biggest index is 2, causing all the problems. Maybe you'll have to come up with some logic to prevent click events until the state is fully updated or use a global variable for the counter and update the state according to that. Happy Tweening!!!
  2. Hi and welcome to the GreenSock forums. This most likely has to do with the fact that your ScrollTrigger instances could be still alive when you switch from one page to the other. Just follow the regular hooks that Vue has for components life cycle. Keep in mind that a Nuxt page is a Vue component after all. Create all your ScrollTrigger and GSAP instances in the mounted hook and pause and kill all your instances in the beforeDestroy hook. Something like this: export default { mounted() { this.tl = gsap.timeline({ /* Configuration Here */ }); }, beforeDestroy() { this.tl.pause().kill(); }, head() { return { title: "Home", }; }, }; If you're still having issues, please try to create a reduced live editable sample in codesandbox using the Nuxt template, showing just the barebones of your app in order to identify what the problem could be. Happy Tweening!!!
  3. Mhh... I'm not sure I follow what you're trying to do, but you can tween the stroke color regardless if the path is dashed or not. I updated my sample to reflect that. If you want to tween the color and then use Draw SVG to animate the path drawing, I'd recommend creating two identical paths one dashed that is visible and the other continuous that is revealed when the Draw SVG plugin works it's magic. @PointC is our SVG wizard around here, maybe He has a trick for this type of stuff. Happy Tweening!!!
  4. Hi, I know two ways of doing that. The first one is to use the attr: {} wrapper in the config object to tween the value directly on the SVG stroke attribute. The second one is to use a class to set the stroke color of the path and use the CSS Plugin to change that color. In this sample the path in the left uses the CSS Plugin to update the color via the element's style and the path in the right uses the stroke attribute directly: https://codepen.io/rhernando/pen/KKaPVrx?editors=1010 Keep in mind that the path SVG element can have a style attribute applied to which is what the CSS Plugin uses to update the color as well as the stroke presentational attribute. Happy Tweening!!!
  5. Hi and welcome to the GreenSock forums. This most likely has to do with some part of your code running on the server, most specifically a GSAP code. My guess is that you're using regular selectors like ".my-class" or "#myId" in your GSAP instances. In that case GSAP runs the code you posted and that tries to access the document object. Since neither the document or window object are found in server side rendering, the build process is returning such error. There are two ways to solve this: Use refs instead of regular selectors. Even with these type of libraries (Gatsby, Next, Nuxt, VuePress, Gridsome, Sapper, etc.) generating static content, they still abide to the rules of the framework they are based on, so you can use all the tools provided by them. I'd recommend you to get a bit more familiar with React and Gatsby regarding refs and DOM content. Keep using regular selectors ONLY if your site content is 100% static and never changes. From the moment the content in one of your pages becomes dynamic, you'll have to use refs and cleanup to avoid errors. If you choose this route, then you add to your code a little conditional block to check if the document and/or window object do exist: if (typeof document === "undefined") return; gsap.to(".my-class", {/* Config Code Here */}); Finally, if possible please provide a small code snippet from your project or a reduced editable sample in codesandbox. That makes debugging simpler and faster. Happy Tweening!!!
  6. Hi and welcome to the GreenSock forums. Well, considering that GSAP works in JS, learning that would be the first step in the right direction. After learning JS is up to you and what you intend to do as a developer, besides creating GSAP based code. In my case I took time to learn PHP and a bit of Python, as well as SQL in order to work as a full stack developer. Funny thing is that, up to this day no one has payed me a single dollar for working with Python, and the last time I got paid for working with PHP was about 4 years ago, when I made a custom WordPress template. Since then all my work has revolved around JS using GSAP, PIXIJS, React and Vue on the client side. Now using React and Vue implies also learning all their environment as well (Routing, Stores, Transitions, Static Site Generators, etc). On the server I normally work with Node, Express, MongoDB, SQL and GraphQL (Apollo and Prisma). Finally now a days is becoming more required to learn Typescript as well, so I'd recommend get your feet wet with that. Also learn Git and testing frameworks such as Jest, Mocha, Chai and if you can learn about CI/CD. But Git is essential these days and also helps to keep the progression of your work better structured and makes it easier work with other developers. It might seem a bit daunting all the things I listed here, but just start with one thing at a time. For example get to know JS and typescript. Then jump into either React or Vue. For example I learned how t use React first and then learning how to use Vue was really simple, since most of these frameworks work in a similar fashion. Finally learn to navigate API docs, that will make learning new frameworks super simple. This is just a matter of practice in my experience, so don't get discouraged if something doesn't work and always try to learn the reasoning behind a solution. While copy/paste will certainly get the job done, you'll learn nothing and the next time you face a challenge you'll be in the same spot, while learning the how and why something works allows you to solve issues by yourself. Happy Tweening/Learning!!!
  7. Yeah, you ran into some scope issues. This can be solve really simple like this: useEffect(() => { gsap.registerPlugin(ScrollTrigger) const tl = gsap.timeline() // PROBLEM CODE var firstIntensity = { value: intensity }, secondIntensity = { value: 2 } tl.from(redRef.current, { y: '100%' }) .to( firstIntensity, { value: 2, ease: 'none', onUpdate() { setIntensity(parseFloat(firstIntensity.value.toFixed(2))); } }, 0 ) .from(greenRef.current, { y: '100%' }) .to( secondIntensity, { value: 0, ease: 'none', onUpdate() { setIntensity(parseFloat(secondIntensity.value.toFixed(2))); } }, '<' ) .from(blueRef.current, { y: '100%' }) // END PROBLEM CODE ScrollTrigger.create({ animation: tl, trigger: pinnedSectionRef.current, start: 'top top', end: '+=4000', scrub: true, pin: true, anticipatePin: 1 }) }, []) The reason for using parseFloat there is because toFixed returns a string, so in some cases is better to set it back to a number just in case. Some JS libraries are quite picky about that type of things: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed#return_value Happy Tweening!!!
  8. Hi and welcome to the GreenSock forums. There are a few issues with the code you posted. First, you're wrapping all your relevant code inside a single useEffect hook, which depends on the value of the state property intensity. The problem here is that, if you want to update the value of intensity using a GSAP instance, you can't create and run that instance inside a hook that is called when the same value is updated. You can easily create a bug that will start an infinite loop or a potential huge memory leak. Use a useEffect hook with an empty dependencies array in order to run that only when the component is mounted. Also don't run the registerPlugin method on a hook that will be ran several times, there is no need for doing that. Second, try to create and store your GSAP instances and ScrollTrigger instances using the useRef hook in order to create instances that will persist throughout re-renders. Right now every time the intensity value is changed you'll end up re-creating the GSAP instance, which might not be necessary, same thing with the ScrollTrigger instances. Also this allows you to easily kill your instances when the component is unmounted during the cleanup stage. Third, state values are meant to be updated only by their corresponding callback, that's the whole reason why the useState hook creates two elements the variable holding the state value and a callback that updates such value. With this tl.to(intensity, { value: setIntensity(0.5) }) basically you're telling GSAP to update something that already has a method to be updated. You can update the value of a dummy object and using an onUpdate callback you can pass the setIntensity method and the value being tweened as the new state value: gsap.to({value: 1.5}, { value: 0.5, onUpdate: setIntensity, onUpdateParams: [value] }); Finally, for what I see in your code, you're not really using the intensity value anywhere else in your code. You're only passing it as a prop to the blob component, so that's another reason to remove it as a dependency in your useEffect hook. As you mentioned a reduced live editable sample in codesandbox would prove very helpful to understand what you're trying to do. No need to set up the whole THREE code, since it seems to be irrelevant to the question at this point, just a simple one describing what you're trying to achieve. Happy Tweening!!!
  9. Hi, I updated the codesandbox sample to use the latest versions of React, Router, Transition Group and GSAP: https://codesandbox.io/s/mqy3mmznn As you can see while is better structured the code is basically a carbon copy of the previous example, so if you're still having issues with this code, honestly it has to be something else, because I didn't had to change anything to make it work, just copy/paste. Happy Tweening!!!
  10. Hi and welcome to the GreenSock forums. The problem is that the method doesn't have access to the updated state since is created again everytime the component is rendered, which happens when the state is updated. The same thing happens with the GSAP instance, on every re-render is created again. The best approach to control GSAP instances using a state property is to store the GSAP instance in a reference (with useRef) so it persists when the component is re-rendered. Finally with a useEffect hook, passing the state property you want to react to as a parameter in the array, toggle the GSAP instance. Something like this: const [isPlaying, setIsPlaying] = useState(true); /* Creatin Bar Refs */ let bar1 = useRef(null); let soundRef = useRef(null); const bar1Anim = useRef(gsap.timeline({ paused: true })); // This will run only whe the value of isPlaying is updated useEffect(() => { bar1Anim.current.paused(!isPlaying); if (isPlaying) { soundRef.current.play(); } else { soundRef.current.pause(); } }, [isPlaying]); useEffect(() => { bar1Anim.to(bar1.current, 0.2, { scaleY: "random(0.2,1)", ease: "none", yoyo: "true", repeat: -1, repeatRefresh: true, }); }, []); You can use functional updates directly on your DOM elements to update the component's state, which can be a bit more clearer sometimes. Finally try to update to GSAP 3.x syntax if possible and also try to create a live editable sample, which makes debugging far simple and fast. Happy Tweening!!!
  11. Hi, Right now I don't have a lot of time to dive into this, but taking a quick look at your code and Blake's sample there are a few things I spotted. First the approach you're using to set the initial position of each element is not working, as every element is above it's parent container. Second the proxy element that Blake uses as target of the Draggable instance is an actual DOM node, in your case you're using an object, which is not working. If I was you I'd first manage to position the elements correctly. Then create and successfully run the animations to move the elements up and down. Finally create the Draggable instance to actually manage those animations. Happy Tweening!!!
  12. Which version of styled components are you using? Styled Components' API do grant access to the DOM node in the component using refs: https://styled-components.com/docs/advanced#refs Now in some cases there shouldn't be too much of an issue using regular selectors in Gatsby/Next projects, just be sure that the content in the page/component is completely static and that is generated in the server using SSR. In those cases nothing will change in the DOM tree after the server renders the page and sends it to the client. @jh3y that was an idea that I entertained for a while, but at the end it's far easier and simpler to just use GSAP in a regular fashion than create a set of components that handle all the DOM node operations for you. At the end you still need to access the ref and what happens if, for example, you need to animate a nested SVG tag with inline attributes, in that case you have to wrap the component with the GSAP one and still clone it and go through all that which is a lot of hassle for that. IMHO is best for people to stop demonizing the fact that using imperative code in React apps is the devil's work, it's just JS that, if used correctly and safely, doesn't have any security or another side effects. Of course this is just my two cents on the subject, if you feel that creating something like that fills a void in the current React+GSAP scenario in the web, then go ahead and dive into it, we'll be here to help as much as possible in the process. Happy Tweening!!!
  13. Hi, I'm not completely clear on what you're trying to do and how you're approaching it, so correct me if I'm wrong. For what I see you want to use the ScrollTo plugin to go to specific sections from the header, but you don't want the ScrollTrigger instances to do anything in those cases, right? I think the best option could be to use the trigger boolean as an effect instead of just reading it's current value. Let me explain. Now you're checking the value of the shouldFire boolean: if (shouldFire) {} but if instead of that your code reacts to the change in that value to do everything. The sequence could be something like this: Click the button in the header, toggle the shouldFire value and store, also in the state, the target that should be passed to the ScrollTo plugin. Inside a useEffect hook in the component with the ScrollTrigger instances, disable all the ScrollTrigger instances when that value is false. Inside a useEffect hook in the header component create and play the GSAP instance that will scroll the screen to that particular place. Inside the same useEffect instance in the component with the ScrollTrigger instances, restore and update them as you see fit. Also I would avoid just toggling the shouldFire value and instead of that I would pass the desired value for the boolean as an argument in the method, perhaps one of the problems you're facing is that this is being triggered far too many times toggling the value too much. Another thing that caught my attention is the fact that you're using async/await with the method created by useState. I'm not familiar with whether or not that method actually returns a promise, perhaps if you try the sequence I suggested, you can remove that, because is definitely not doing much. Finally when it comes to working with Next, keep in mind that process.browser is an environmental value available to webpack, so that is not going to be available in production code. It was discussed in this topic: Happy Tweening!!!
  14. Hi, I tried to set an example but I'm a bit short of time. The main problem you're facing is the use of the <Switch> component. Keep in mind that Switch renders the first match it finds for the current path, which means that it will remove the previous route immediately without allowing the <Transition> component to run it's normal course. I found this old sample of using switch with transition group. It has very old dependencies, but looking through the docs in both the router and transition packages, it should work or just require a few minor tweaks: https://codesandbox.io/s/mqy3mmznn?file=/components/routes.js Now everything has it's pros and cons. Using switch gives you a nice and simple way to handle 404 routes without too much hassle but, as you can see in the code, you have to create the same animation for every route change. Not using switch (for that just comment out the <Switch> tags in your code), gives you the flexibility to go totally nuts in terms of creating different animations for every route, which is not something you see too often, but you have to create a more convoluted way to handle 404 routes. Happy Tweening!!!
  15. Hi, The useEffect() hook can return a function that is executed when the component is unmounted (something React Router does for you), in that function you have to kill your ScrollTrigger instances in order to prevent that type of issues, something like this: useEffect(() => { // All your code here return () => { // Cleanup here, kill your ScrollTrigger instances here }; }, []); I don't have a live sample to show right now. I'll try to make something simple during the weekend. Hopefully this will be enough to get you started right now. Happy Tweening!!!
  16. Hi, You can create a React app sample with GSAP using Codesandbox, give it a try to create a reduced sample that illustrates the issue you're having. You can even create sandboxes from Git branches if you prefer that approach. Happy Tweening!!!
  17. Hi and welcome to the GreenSock forums. One problem I can spot is that your useEffect() hook is missing the array of dependencies, so that is basically being executed everytime the component re-renders. Finally, do your best to create a live reduced sample using Codesandbox in order to have something live and editable that illustrates the issue. Makes debugging super easy and fast. Happy Tweenng!!!
  18. Hi, There are a few things you need to consider. Will the state and/or props of each SingleDot component change at any point during it's lifecycle? If the state changes, then yeah, store all initial data using useRef and create the callback using useCallback. If the state doesn't change just use the React.memo() in order to prevent unnecessary re-renders. Also, do you really need to make every dot a component? Give that a good thought and use the magic word a guy I worked with some time ago asked all the time: Why? Every architectural decision in your app should be followed by asking that question a few times, until you reach a point where is clear that is the best option or you saw a better way to do it. If you don't actually need every dot to be a component, perhaps create a Dots component that has all the dots in it and handles all that data. Maybe all the process described above might seem extreme for something small or not too complex, but it's highly recommended to use best practices and try to optimize everything, regardless of size and performance impact. In small apps performance could be unnoticeable most of the times but using that approach can be useful when landing a job working on an ongoing project and/or working with a large development group where those things are required. Always use what I call the Forrest Gump axiom: "Life is like a box of chocolates, you never know what you're going to get". Always work thinking that at some point you'll need to make all those decisions, read about them and experiment, so when the time comes to actually make that decision you can give a solid answer every time the project manager asks: "Why?" Happy Tweening!!!
  19. Hi, You can create a live editable sample of an app bundled with Parcel in codesandbox. Just create a VanillaJS sandbox, I believe that by default those projects actually use Parcel to bundle the app. All you have to do then is just install all the dependencies you need. https://codesandbox.io/ Happy Tweening!!!
  20. Hi, I don't have time to create a sample right now but this is a fun idea, good job!!! One approach I'd use would be to make every dot a single component and store it's initial position using useRef(), since that hook stores data in the current property that doesn't change during re-renders. The second approach is to animate X and Y, as Zach already suggested, and you don't need to reverse the animation, simply create an onComplete callback and animate both values to zero. This seems to do what you're after: const tlMouseEnter = (t) => { gsap.to(elem, { x: r(50), y: r(40), duration: 2, ease: 'power2', onComplete() { gsap.to(elem, { x: 0, y: 0, duration: 2, ease: 'power2' }) } }) } Happy Tweening!!!
  21. Rodrigo

    Flip with React

    Hey, I created a simple example based on this CodePen from the GreenSock collection (I assume that this is what you're after). Now for some reason in Codesandbox this didn't worked so I had to create a repo and publish the sample in the corresponding GitHub page. This is the repo url: https://github.com/rhernandog/gsap-flip-react Here is the meat of the code: https://github.com/rhernandog/gsap-flip-react/blob/master/src/App.js Here is the live sample: https://rhernandog.github.io/gsap-flip-react/ Based on your initial post in the thread, I take that you want to remove the elements from the DOM when they're animated out and mount them back, once they're animated in. Unfortunately, for work reasons I don't have enough time to add such feature to this sample, but this should be a good starting point for it and hopefully it will help you. When I have more time in the future I hope to be able to improve this sample. Happy Tweening!!!
  22. Hi and welcome to the GreenSock forums. Keep in mind that the setup method in the composition API runs before the component instance is created and it's purpose is to store the component's reactive data. In your particular case title is just a reference to a reactive data, nothing more. Is not a DOM node that GSAP can actually tween in the App. What you could do is create a reference that later in the component's lifecycle, can hold a GSAP instance, but it seems overkill IMHO. The recommendation for using GSAP with Vue is to attach a GSAP instance to the component's instance in the mounted hook and use refs in the template to identify the DOM node you want to use, like this: <template> <div ref="myElement" /> </template> Then in the script tag: import { gsap } from "gsap"; export default { mounted () { this.myTween = gsap.to(this.$refs.myElement, { /* GSAP Config Here */ }); } } Keep in mind that the main purpose of the composition API is to avoid giant monolithic reactive setups in large applications, or smaller ones that need a scalable flexibility, but it shouldn't be a place to define your GSAP instances. Happy Tweening!!!
  23. Hi, As Zach mentions, this is related to the way Styled Components work, but lucky for us, they have made some strides in order to facilitate the access to the actual DOM nodes. Basically you have to pass a ref to the styled component you want to target in your GSAP instances, store that using the useRef hook and then just use ref.current in order to access the DOM node. This is a super simple example that takes the code from your file, without any errors: https://codesandbox.io/s/gsap-scrolltrigger-styled-components-pzqy2 Hopefully this helps you get your site going. Happy Tweening!!!
  24. Yeah, that is completely expected. Keep in mind that the element passed to the ref callback is a React component not a DOM element, so while GSAP is not going to complain about it (you're passing an object), it won't work. Don't use a ref callback, use a regular function passed to the component's ref: const images = useRef([]); // sometimes img can be null, so you'll get an error const createGalleryRef = (img) => { if (img) { images.current.push(img.imageRef.current); } }; return ( <Img fluid={image.childImageSharp.fluid} ref={createGalleryRef} /> ); In the images array you will have a series of <img /> DOM elements ready to be used by GSAP. Finally, if possible create a codesandbox live sample to see what could be the issue. Happy Tweening!!!
  25. Hi, Gatsby image actually does gives you access to the <img /> in the DOM: https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-image/src/index.js#L271-L327 https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-image/src/index.js#L364 All you have to do is pass a function to the ref={} prop in the Img component. That function gets called with an object (React instance) as the first parameter. Then as you can see in the codebase you can access the image reference as imageRef.current (reference created with React.createRef()). In that function you can add the ref of each image to an array (if you want to do something with each image of course) or add it to a specific ref of your own, it's a bit of an extra work, but at least the gatsby-image component gives you access to the DOM node. Here is a super simple example of getting the image ref: https://codesandbox.io/s/nifty-engelbart-0bd0y?file=/src/components/image.js Be aware though that gatsby-image has it's own animations and transitions when the image has been loaded, so you have to be a bit careful, especially if you're planning on using .from() instances with GSAP. Be sure to apply GSAP after the image has been loaded in order to avoid any issues. Finally I'm far from being an expert in Gatsby, I really prefer NextJS when it comes to a React tool for creating web apps with SSR, SEO, etc. Happy Tweening!!!