Jump to content

Rodrigo last won the day on March 17 2019

Rodrigo had the most liked content!


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Rodrigo

  1. Hi, Well since you're using react router and you have an overlay covering the entire content of the site perhaps using transition group with GSAP might not be necessary. The main question is this: Is the color overlay transparent in any degree or solid color?. Depending on the possible answers, these are the scenarios. Case: Solid Color Overlay The color overlay hides all the content, so there's actually no need to use transition group in order to animate each route's component's mount/unmount. Just use GSAP to animate the color overlay and using an onComplete callback, use history (a dependency used by react router and passed as a property to every component in a <Route /> wrapper) to push the next route. In this scenario the only catch is that the overlay component has to reside inside of a route (you can put it in the root route "/" or wrap it in a <Route /> component), which shouldn't be too complicated since I'm assuming it has a fixed position and a z-index that puts it on top of everything. It could be like this: Overlay component, as you can see the idea is to render it in every route, that's why there's no exact attribute in the Route component. Also the Route component is passing it's own history property to the overlay component class App extends Component { render(){ reuturn <div> <Route path="/" render={ props => <ColorOverlay history={props.history} /> } /> // the rest of the routes here </div>; } } Color overlay component import { connect } from "react-redux"; import { toggleOverlay } from "./actions"; // depending on your actions path class ColorOverlay extends Component { constructor(){ super(); this.overlayTween = null; this.overlayElement = null; this.overlayTweenComplete = this.overlayTweenComplete.bind(this); } overlayTweenComplete(){ const { history, targetRoute, toggleOverlay } = this.props; // here's all the magic // at this point the overlay is covering all the content // so we can change the route using history passed in the props history.push(targetRoute); // now the route has changed and we can reverse the animation // revealing the new rendered component toggleOverlay(false); } componentDidMount(){ this.overlayTween(this.overlayElement, 1, {/*config here*/, onComplete:this.overlayTweenComplete, paused:true}); } // we're updating the visibility via redux, so we look for // updates in the props componentDidUpdate(prevProps){ const { overlayVisible } = this.props; // check if the visible prop is the one that changed if ( prevProps.overlayVisible !== overlayVisible ) { overlayVisible ? this.overlayTween.play() : this.overlayTween.reverse(); } } render(){ return <div>/*your overlay html here*/</div>; } } const mapStateToProps = state => ({ overlayVisible: state.overlayVisible, targetRoute: state.targetRoute }); const overlayActions = { toggleOverlay }; export default connect( mapStateToProps, overlayActions )( ColorOverlay ); In the snippet I'm making some assumptions that is worth clearing up. First I'm assuming that since you want an overlay over the content using react router's <Link> or <NavLink> is not necessary for every button that should trigger that functionality. If you need a button, link or menu item to not use this overlay transition, then use the normal way of doing that with react router. Second you should have an action creator to toggle the overlay visible property in the store and the target route. The overlay visible is just a boolean and the target route is the string of the route to push into react router, so it should be in this format "/about", "/products", etc. So instead of using a link or navlink with the to="" attribute to change the route, just use an onClick event to update the target route first and then play the overlay animation: import { connect } from "react-redux"; import { toggleOverlay, updateTargetRoute } from "./actions"; // depending on your actions path class MainMenu extends Component { constructor(){ super(); this.buttonClickHandler = this.buttonClickHandler.bind(this); } buttonClickHandler(target){ const { toggleOverlay, updateTargetRoute } = this.props; // update the route in redux updateTargetRoute(target); // now that the target is updated toggle the visible state toggleOverlay(true); } render(){ return <div> <button onClick={this.buttonClickHandler.bind(null, '/')}>Home</button> <button onClick={this.buttonClickHandler.bind(null, '/about')}>About Us</button> <button onClick={this.buttonClickHandler.bind(null, '/products')}>Products</button> </div>; } } const menuActions = { toggleOverlay, updateTargetRoute }; export default connect( null, menuActions )( MainMenu ); (the syntax highlighter is messing up the color, but the code should work). The reason to control both the play state of the tween and route of the app via redux is that it could be a slight delay while setting the target route and also the fact that if you need the value elsewhere is already in the store. Something like this should be able to animate the overlay before changing the routes and then remove the overlay after. Case: Transparent Color Overlay In this case the content under the overlay will be visible so my guess is that you might need to actually animate one route being unmounted and the other route being mounted. If that's the case take a look at this sample (codepen doesn't have the features to import and mimic real work flows so I use codesandbox for more complex React & Gsap samples): https://codesandbox.io/s/mqy3mmznn Happy Tweening!!!
  2. Hi, Does this comes close to what you're trying to do?
  3. Hi, There are two things that I can spot in your code. First, everytime you click on a menu project you're adding instances to a timeline, therefore that instance gets longer and longer. Second you're not keeping track of the element that's currently expanded (if any). Normally when working with this type of projects, is a recommended pattern to keep track of the active element so you can unset that element as active when you either toggle that state on the element or select a new one as active. In your case if you click on project 1 that should be selected as active and start the animation. Then if you click on that element again by being the active element you can reverse the tween and unset it as the active element. Same thing if you click on another element: unset the active element, reverse the tween, set the another element as active and finally start that tween. Unfortunately you caught me with little time now to create a sample or play with your codepen sample. Later I'll be able to circle back to it. In the mean time use the component's state to keep track of the current active element (you're already passing a unique id in a method so you can use that) and the element being clicked. If there's an active element and the id's are the same then just reverse the animation, clear it ( https://greensock.com/docs/TimelineLite/clear() )and set the active property to null. If there's an active element and the id's don't match, set the active to null, reverse the timeline, clear it, set the new active, create the timeline and restart it. Happy Tweening!!!
  4. Hi Paul, I don't know how you're bundling your code, so I'll assume babel and webpack or perhaps create react app. If you're using the HTML plugin for wepack, then webpack is placing the scripts tags in your index file for you, above the </body> closing tag, so you could add the CDN links between the <div> where the app is being placed and the closing body tag. <html> <head></head> <body> <!-- HERE REACT RENDERS THE APP --> <div id="app"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.4/TweenMax.min.js"></script> <script src="js/gsap/plugins/MorphSVGPlugin.min.js"></script> <!-- WEBPACK ADD THE SCRIPTS HERE --> </body> </html> If you're not using the HTML plugin then just add them manually before the tag where you're putting the bundled code: <html> <head></head> <body> <!-- HERE REACT RENDERS THE APP --> <div id="app"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.4/TweenMax.min.js"></script> <script src="js/gsap/plugins/MorphSVGPlugin.min.js"></script> <script src="js/app.js"></script> </body> </html> If you're using create react app, unfortunately I'm not at all familiar with it, I prefer to create my own startup code, but perhaps you can use this links: https://github.com/facebook/create-react-app/issues/3007 https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#using-the-public-folder Finally if you could provide more info about it, it'd be very helpful, such as what set up you're using and that particular part of your index.html file. Happy Tweening!!
  5. Yikes!!! there was a missing part in the code. This: paintOutTimerCreator(){ if ( this.painOutTimer ) { this.painOutTimer.restart(true); } else { this.painOutTimer( 4, () => this.paintOutTween.play(0) ) } } Should be this: paintOutTimerCreator(){ if ( this.painOutTimer ) { this.painOutTimer.restart(true); } else { this.painOutTimer = TweenLite.delayedCall( 4, () => this.paintOutTween.play(0) ); } } I forgot to add the instantiation of the delayed call, sorry about that. Happy Tweening!!
  6. Hi, There were some issues with your code. In react inline styles are not applied like in regular HTML, take a look at the official React docs: https://reactjs.org/docs/dom-elements.html#style In that case is better to move the styles to the css file (SASS in the codepen). Then you're creating the TweenMax instance before the react class is even created, so when GSAP is instantiated there's no DOM element with the ID you're pointing to in the GSAP instance, so that throws an error as well. This code seems to do what you're expecting: class SvgReddit extends Component { constructor(){ super(); this.startPath = null; this.endPath = null; this.svgTween = null; } componentDidMount(){ this.svgTween = TweenMax.to(this.startPath, 1, { morphSVG:{shape: this.endPath, shapeIndex: 12}, fill: "#ff0000",ease:Bounce.easeOut }); } render(){ return ( <div> <svg id="Layer_6" data-name="Layer 6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 136.66 171.86"> <defs></defs> <title>redditlogo</title> <path ref={path => this.startPath = path} id="start" className="cls-1" d="..." /> <path ref={path => this.endPath = path} id="end" className="cls-2" d="..." /> </svg> <h1 onClick={ () => this.svgTween.play() }>Devil</h1> <h3>or</h3> <h1 onClick={ () => this.svgTween.reverse() }>Angel</h1> </div> ); } } Also this code should be compatible with the latest version of react and respects the deprecation of the strings refs by using the callback to assign the DOM nodes in a declarative way. Happy tweening!!
  7. Is the "paint out" animation happening after some time or an event handler, like click, hover, etc.? This is the most important part in order to choose the best course of action. I'm going to guess based on the code you added in the first post. If the animation happens some time after the "paint in" animation is completed then GSAP has you covered. All you have to do is create a delayedCall instance and execute a function to start the other animation. You can create a method in your react class in order to simplify the whole process. Something like this: class MyComponent extends Component { constructor(){ super(); // this method will create and restart the delayed call this.paintOutTimerCreator = this.paintOutTimerCreator.bind(this); this.paintInTween = null; this.paintOutTween = null; this.paintElement = null; // this is the DOM element being animated this.painOutTimer = null; } componentDidMount(){ const { paintOutTimerCreator } = this; this.paintInTween = TweenLite.to(this.paintElement, 1, { width: "110%", zIndex: "900", ease: Expo.easeInOut, delay: 2.1, onComplete: paintOutTimerCreator }); this.paintOutTween = TweenLite.to(this.paintElement, 1, { width: "0", right: "0", left: "initial", ease: Expo.easeInOut, paused: true}); } paintOutTimerCreator(){ if ( this.painOutTimer ) { this.painOutTimer.restart(true); } else { this.painOutTimer = TweenLite.delayedCall( 4, () => this.paintOutTween.play(0) ); } } // I'll assume that this part of your code is working render(){ return(...); } } As I said this code is assuming a lot of things, so it would be nice to have a better grasp of what you need. As always if you could provide a reduced sample in codepen, it would be very helpful. Happy Tweening!!!
  8. Hi, The thing here is that componentWillUnmount is not designed for that actually. From the React docs: Basically the lifecycle methods are there only as hooks or event emitters, soto speak, pretty much like GSAP's own callback system. The onComplete, for example instance is not going to wait for something else to happen before executing the provided callback. Like that componenWillUnmount is not going to wait for some animation or anything else that might take more time than it's own execution to complete and remove the component. So that's not a good place for any of that type of code. In fact this particular hook is not as used as others lifecycle hooks. A good thing would be to know exactly what you're trying to do.For your code I assume that you're still working on the loading overlay from the other day. If you're trying to simply animate out the component without removing it from the DOM then I'd recommend a simpler approach, such as a regular event handler like a button or something similar. This sample animates a DOM element with a delay (wait two secs) and then with the button you can toggle the animation Another option is a callback in the GSAP instance itself. As I mentioned it'll be best to know exactly what you're trying to achieve and take it from there. Happy Tweening!!!
  9. Hi and welcome to the GreenSock forums!!! This always seems more daunting that it really is. All you have to do is create a reference to the element you want to animate, create a GSAP instance for the animation in the componentDidMount event and then play the animation when you need it. Here's a simple codepen that does exactly that: Now if you check the code you'll see two references set to null in the constructor method, the loader wrap and the reference that later will be used to hold the GSAP instance. These are useful if you later want to use them again so you can set the position of the loader wrap to make it visible again and then you want to play the animation to remove it. The loader wrap reference is set in this code: <div className="loader" ref={div => this.loaderWrap = div} > THIS IS THE LOADER WRAP, PUT YOUR SPINNER HERE!!! </div> Here the ref property is passed a callback that takes the React node being mounted as a parameter and sets it as the reference called loaderWrap. In this case the node is the actual div that will be rendered in the DOM, which is what GSAP uses to animate. Finally the GSAP instance is created in the componentDidMount method to ensure that the DOM nodes are actually mounted and rendered by that time. Happy Tweening!!
  10. Hey Beau, There's absolutely nothing wrong with the React part of your code, that's working as expected. You're generating the refs for each DOM node and using them in the componentDidMount method. The issue has to do with the GSAP part. Right now you're creating an instance in the timeline for each SVG element but you're not passing a position parameter, so GSAP is putting each instance at the end of the timeline, therefore each element is animated in a sequential way. In order to achieve what's happening in the second codepen you can use two approaches: Option One Add every single instance at the start of the timeline using the position parameter at 0 and add random delays to each one in order to create the overlapping. const tl = new TimelineMax(); waves.forEach( item => { tl.to( item, duration, {opacity: random(), delay: randomDelay()}, 0 ); }); That zero at the end tells GSAP to add the instance at the very start of the timeline, ie, time zero. The random functions are for creating random opacity and delay numbers. Option Two Create a random position parameter for each instance based on the duration of the timeline when that particular instance is being added to the timeline or by forcing the timeline to have a specific duration using the (you guessed... the duration method). Now this might be a little more complicated because you'll have to access the duration of the timeline or set a fix duration for it. Check this from the API: https://greensock.com/docs/TimelineMax/duration() So I'd recommend using option one if you can. Finally check this from professor @Carl in order to get a great grasp of the position parameter. https://greensock.com/position-parameter Happy Tweening!!
  11. Hey Tomsah, getting back to it now. Not at all, your GSAP instance will be available to every method in the global scope of your module or file. Some people like to attach the GSAP instance to the react component like this: import React, { Component } from "react"; import { TimelineLite } from "gsap"; class MyApp extends Component { constructor(){ super(); this.projectTl = new TimelineLite({paused: true, reversed: true}); } render(){ return(...); } } This allows to pass some of the components props or state properties to GSAP's callback system and stuff like that or if you need to reference a specific method from your component in a callback, or perhaps update the state using onComplete, onRevereseComplete or something like that. I don't see anything wrong with using stagger to do that. Keep in mind though that the stagger methods return a series of GSAP instances with different position parameter applied to them, based on the stagger time you provide. I recommend using React transition group only when the animation is related to the component being mounted/unmounted. For example if you want the element to be mounted before the animation starts and unmounted after the animation is complete, or if you're looking for some finer detail control over the lifecycle methods and associate them to the animation, you can use transition group. Other wise my advice is to stick with GSAP and use it in he usual way with react. Transition group might be a little too much if you're just starting with react and GSAP. If the animation only affects the elements in that component, then there's no reason to have it in a different component. If you want to target some specific callback from a parent component in the timeline, then you can pass it as a property. Finally if you need to access the GSAP instance in different components in your app, perhaps you should think about using redux, mobx or some other store solution in order to access the timeline in different components. Now I have never used redux to store a GSAP instance, but I can't think about a reason against though. Happy Tweening!!!
  12. Hi Unfortunately I ran out of time to get a proper answer, I'll get back to it later tonight. In the mean time take a look at this: https://reactjs.org/docs/refs-and-the-dom.html This: And this: Hopefully this is enough for now. Happy Tweening!!
  13. Hey Beau, I understand the frustration. The reality of things is that React works in a declarative way and the discourage to use string refs and find dom node has mostly to do with some specific issues and some excessively paternal approach by the React team. The paternal issue comes from the fact that the deprecation of string refs has to do with some edge cases and because of a few things that could go wrong the entire API is changed for everyone. Take GSAP for example, Jack does His best to ensure that GSAP serves as many scenarios as possible but never compromises performance, file size nor legacy support, and when those changes do come, believe me there is a lot of questions and samples going around before the decision is made. But that's how GSAP works and perhaps we've been a little spoiled by that, but that's how serious Jack and Carl are about how this works. IMHO there's nothing wrong with using string refs and fond dome node if you know what you're doing. At some point developers have to take responsibility for the code they write and not expect that the frameworks make all kind of loops and hoops to ensure that every blunder is supported. As I mentioned in another post yesterday this is some very specific issue, because you have to take in consideration the lifecycle of your app and it's components in order to make the best decision. There are some cases that you can safely reference the elements using the classic get element by id if, when you create the variable you're sure that the DOM node will be rendered in the app. In other cases if you're not sure is better to use refs. This simple codepen goes into the detail of how refs are accepted today: In the console you'll find an array with all the rendered elements that can be used with GSAP. This is the so called "React way" for referencing DOM nodes. Now the pain comes when you're using a third party component that doesn't expose the DOM node, like this case: import ThirdParty from "third-party"; const elements = ["label-1", "label-2", "label-3", "label-4"]; class MyApp extends Component{ constructor(){ super(); this.elements = []; } render(){ return <div> { elements.map( e => { <ThirdParty key={e} ref={ node => this.elements.push(node) } /> }) } </div>; } } In this case the array will have a collection of the four React components being rendered and not the nodes being added to the DOM. In this case find dom node comes in handy because it allows to reference the actual element in the screen. If the plan is indeed remove find dome node from ReactDOM then things will get more complicated, because developers will have to bake their own version of this component in order to expose the ref to the DOM node or the developers of third party components will have to come up with some way to find those references. One of the simple ways to do this could be for components to accept an id property in order to later reference that and get access to the DOM node. Finally don't get discouraged from using GSAP with React, there are ways to use it and the more is being used, the React team will become aware that to create complex animations and rich environments GSAP is the way to go and they'll have to come with some way to actually gain access to the dom elements. Right now we have to keep using this type of approaches with the hope that things will change and become simpler in the future. Also keep in mind that the article you mention was written when React was in version 15 and string refs and find dom node were not deprecated and that wasn't a long time ago, unfortunately this world moves way to fast and doesn't wait for anyone... Happy Tweening!!!
  14. Hi Tomsah and welcome to the GreenSock forums. The issue here lies in the fact that you're creating a new instance of the timeline each time you click on the toggle button. The real kicker is that, since the timeline is on a global scope, everytime the slideMenu method is called, GSAP says: "ok, so your calling a method that adds this instances to the timeline and since you're not specifying where you want them, I'll add them to the end". So everytime you click on the toggle button the timeline gets longer and longer and longer and longer and... you see where this is going right?? React has components lifecycle methods to help with this. You can create the timeline just once in the componentDidMount method and the timeline will be created and you can be sure that your elements are in the DOM when it happens. Just remove the call to slide menu in the toggle button click handler and add the component did mount call in your component: // add component did mount to the class componentDidMount(){ this.slideMenu(); } // remove the slide menu call in the toggle click handler toggleMenu = (e) => { e.preventDefault(); menuTl.reversed() ? menuTl.play() : menuTl.reverse(); this.setState({ menuActive: !this.state.menuActive }) } On a related note, the more purist React developers will consider using selectors like this a big NO-NO: var projects = document.getElementsByClassName('project'); React is all about doing things in a declarative way, so for getting dom nodes the so called "React way" is to use refs which in some cases can be really painful, specially if you're working with components that you didn't wrote, which makes generating and accessing the refs a bit tricky. I've been using React with GSAP for some time now and this is just my opinion, but in the case of elements that are permanently rendered on the DOM and are always there, there's no issue in doing it like you did. If that's no the case, the one thing you have to keep in mind is if a state or props change could un-mount the component being animated or elements contained in that component that could be animated, because the component could be unmounted and the GSAP instance could be still be active and you'll get an error. In that case you can use componentWillUnmount to kill the animation and prevent an error. Happy tweening!!
  15. Hi and welcome to the GreenSock forums. The way I normally attack this situation is using React's lifecycle methods, most specific the componentDidMount method. With that you know for sure that all the child have been rendered before making the animation. This is a sample that uses a code similar to that. Unfortunately I don't have any samples around with the specific behavior you need but hopefully you'll get the gist of it: Basically when creating each child (which in this case come as a hardcoded array, but getting the data via props shouldn't be much different) you give it a unique ref attribute to each which then you can use to loop and create the timeline or the stagger instance if you like (to create a stagger instance just reach to the array in the props and that should do it), then create the timeline and finally play it. I'll try to whip something during the afternoon that matches what you need. This is another sample but it uses a different approach, since it uses Transition Group since the elements are mounted and unmounted depending on the user interaction. It doesn't use a timeline or stagger, just a simple delay. This relies in the fact that the code is executed very fast (for larger apps you could consider server side rendering or perhaps another approach, once all the elements are rendered, perhaps pagination to render a small chunk of elements) and that the components are rendered in the sequence they exist in the app's data (again hardcoded in an array). But considering the fact that in a real app React will render all those child components at once so we can rely on that delay to create the stagger effect. Pay no attention to the code that's commented out, that's there in case someone needs to use CSS Transitions. The only catch is that the duration const used in the <Transition> component has to be the same that the one used in the GSAP instance, otherwise the component mount/unmount will be out of sync with the animation. https://codesandbox.io/s/7Lp1Q8WBA Hope this helps. Happy Tweening!!!
  16. Rodrigo

    React and GSAP

    Hi, Basically the React and React Router (RR) code came from the samples in this issue of the React Transition Group (RTG) repo, so not too much official documentation regarding this: https://github.com/reactjs/react-transition-group/issues/136 RR basically relies on CSS Transitions mainly and that's kind of the official "way to go" in terms of animating route changes. Also you have to keep in mind that the code also has to accommodate for some problems regarding RR, mentioned in this issues: https://github.com/ReactTraining/react-router/issues/5039 https://github.com/ReactTraining/history/issues/470#issuecomment-345422719 The rest comes from the API of RTG. It might seem a bit daunting at first, but the code is not that complicated actually. What part is giving you trouble?
  17. Rodrigo

    React and GSAP

    @axe-z I haven't played too much with React 16 and GSAP yet but it seems that it hasn't change that much. With that in mind this samples use React 15, Transition group 2.2.0 (latest is 2.2.1) and React Router 4.1.2 (latest 4.2.0) so they should come in handy in order to get you started. Simple component transition using GSAP https://codesandbox.io/s/yvYE9NNW Using React Router to animate the mounting and unmounting of the components: https://codesandbox.io/s/mQy3mMznn Happy Tweening!!
  18. Hi, The thing is that the `onComplete` callback is being used to mount/unmount the component from the App and the DOM. He's using React Transition Group, which is heavily inspired from Angular's ng-animate (at least the old version of ng-animate when you had to pass a callback to onComplete in order to keep track of things), and honestly my personal opinion is that for a simple mount/unmount toggle He's over-complicating things, because I don't think is absolutely necessary to use the component's lifecycle methods in the animations, react transition group does that for you, but everyone has it's own ways so if it's working for Him, that's great!!. Here are two samples of using GSAP with React and Transition Group. This is a simple component toggle, the component gets animated and when the out animation is complete it's unmounted, when the in animation starts is mounted: https://codesandbox.io/s/yvYE9NNW As you can see the `<Transition>` element is just a high order component that adds some features to the regular component using the `addEndListener` attribute. This sample is a todo list using the same set up. Never mind the code that's commented out, I keep that in case someone needs to use... [sigh] CSS transitions (sorry for using the forbidden language in the forums :D): https://codesandbox.io/s/7Lp1Q8WBA Hope this helps. Happy Tweening!!!
  19. Rodrigo

    GSAP and react

    @Carl, @PointC, you guys always know how to put a smile in my face. Is great to feel welcome everytime I pop in the forums. @thomas159, since you want to do a one time stagger the best choice (that I can think of at least) would be to create a timeline in the `componentDidMount` method and add each instance looping through the same array used to add the elements in the DOM. The only caveat is that you need a reusable property in each element of the array for the reference, which in the case of your sample could be the name property of each element. Here's a very simple example: Happy Tweening!!
  20. Rodrigo

    GSAP and react

    If you're using an array with the data you'll use as a reference, then there's no need to use an anonymous function in the ref attribute, just pass the property of the element being used in the map helper: const data = [ {id:1, name: 'box1'}, {id:2, name:'box2'} ] data.map( (item, index) => className={styles.box} key={`box${index}`} ref={item.name}; ); Then reference the element following the same pattern: tl.to(this.refs.box1, 0.5, { backgroundColor:"#F19939" }); Keep in mind that the GSAP instance must be created inside the component's that has the node element. Here's a simple example made in codepen. Is not a lot of code so it should be easy to follow: Happy tweening!!
  21. Rodrigo

    GSAP and react

    Hi, Basically the ref attribute in React, points to the node element being created and then added to the DOM. When you use a function in the ref attribute, the parameter passed by react is the DOM node being created. Now the problem with that code when used in a map array helper, is that is adding a reference to that specific node in the react component. So every time the map helper runs the reference to this.name is updated to that last element. Therefore when the array mapping is complete, the reference in the component is only the last element. render(){ return( '<div ref={ (node)=>{console.log(node)} }> </div>' ); } //the console will return the <div></div> tag This is perhaps one of the sore spots of animating mapped collections with react, how can we reference all the elements created in the map helper. The main question here is, what type of animation you're trying to create?. If you want to do a one time stagger, then perhaps you could create an array and use the map callback in order to push each node element and do the tagger. If you want to access each element independently, then you should give a unique name to each one in the ref in order to reach them when necessary. Right now I'd suggest that you post back with the type of animation you need and we can take it from there. Best, Rodrigo.
  22. Hi, This is a very simple concept I made some time ago for a wordpress site: The main thing to keep in mind is to kill any existing tween and create new ones after getting the new height of the element after the resize event. Is worth noticing that the animations are reeeeeeaaaally slow in order to test the code responsiveness, but you could use any GSAP instance you want. Also it could use some work in order to avoid looping through all the elements and do it just for the active ones. Is not precisely what you're doing but hopefully will help you get started. Happy tweening!!
  23. Hi Anthony, Looks nice!!! Perhaps this could be useful to tween the timescale back to 0 when hovering over the cube and using an onComplete callback to snap the cube to the current face:
  24. Hey Anthony, the delayed call was just a sample of using GSAP and modifying other state properties while the animation (delayed call) is working, nothing more. I agree with you that it has no use in your App . I also agree that using the react gsap enhancer shouldn't offer any upside in this particular scenario. As for configuring eslint globals, why don't you declare them in the .eslintrc.json file? { "env": {}, "parserOptions": {}, "globals": { "PIXI": true, "TweenLite": true, "TweenMax": true, "TimelineLite": true, "TimelineMax": true, "Elastic": true, "stripes": true, "axios":true }, "rules": {} } I always use that when working with eslint. Happy Tweening!!
  25. Sweet demo Anthony!!! always good to see you coming back with awesome stuff!! For performance I would try to avoid using the elastic easing on all the thumbnails when clicking on the expanded image, I would go with a regular easeOut function, perhaps Power2 and use the elastic on the expanded being scaled down. For using react create app, I can't help you. I've baked my own start packages for working with react, angular, vue, etc. so I never use those tools. Also I've never been a big fan of this bundling madness that has taken over front end development, so even today sometimes I keep GSAP outside the bundled files. This is particularly useful when you end up with a bundled file over 1MB because you can create a pre-loader with GSAP. I'm sure Blake can give you some pointers in this regard. I see you're using timescale to speed up/down the animation using the pointer position. But also I saw this: TweenMax.to(this.timeline, currentFace.progressRemaining / 2, { time: this.timeline.getLabelTime(currentFace.id.toString()), ease: 'Back.easeOut', }); Why don't you create one TweenMax instance to rotate to each side, just like the one you already have, using relative values and a repeat:-1 with linearEaseNone in order to create an endless loop, or you can use the modifiers plugin as well, and based on the pointer position update the timescale between 0 and 1 like that you avoid code like the one above and keep it more simple. You can even put those GSAP instances in your component's state or even a redux store and just reference them. Finally I don't know how the GSAP instances could be messing with the App's state. As a rule of thumb, I try to avoid GSAP updating any property of the app's state that could mean a re-rendering that ultimately will affect the instance. If the GSAP instance will update something in the state, is vital that nothing else does because GSAP won't know about that and will keep going with the stored values, unless you reset and create the instance again. For example if a Tween is changing the X position of an object and is not entirely necessary to store that value in the state then don't, in order to avoid something that could mess that up. In your sample I can't see if the GSAP instance is updating anything from the state, so I'd need for you to tell me that. This is very simple examples of some things I made with react some time ago: In this basically GSAP just animates the DOM element and doesn't even know about the app's state. This is using a delayed call and the Tween updates a specific part of the state that nothing else can update. Finally with the button you can change another state property. Finally this is using the modifiers plugin: Hope that this helps. Best, Rodrigo.