Rodrigo last won the day on July 26 2016

Rodrigo had the most liked content!

Rodrigo

Moderators
  • Content count

    1,575
  • Joined

  • Last visited

  • Days Won

    148

Rodrigo last won the day on July 26 2016

Rodrigo had the most liked content!

Community Reputation

1,950 Superhero

2 Followers

About Rodrigo

  • Rank
    Advanced Member

Contact Methods

  • Skype
    r_hernando

Profile Information

  • Gender
    Male
  • Location
    Santiago - Chile

Recent Profile Visitors

19,869 profile views
  1. Countdown with rolling numbers

    Hi and welcome to the GreenSock forums!! A looooooooong time ago I made this for another question in the forums. It uses the draggable tool to move the numbers but you could easily use it to get started with what you need: I made a fork of it and changed a bit to simulate what you need: Hopefully this helps. Happy Tweening!!!
  2. Hi, This seems to do what you're trying to achieve or what I understood at least. I'll go in as much detail as possible. First I got rid of almost all your code, nothing against your code, is just my unwillingness to go through a lot of code I didn't write, normally is faster to just rewrite things. I went as much as possible in the react way of doing things. So I removed the global scope timeline for the menu and added it to the component, like this: class App extends React.Component { constructor(props) { super(props); this.state = { menuExpanded: false, targetPanel: null }; // menu items this.menuItems = []; // menu tween this.menuTl = new TimelineLite({paused:true}); } componentDidMount() { this.createMenuTween(); } // bring the project to view// createMenuTween = () => { this.menuTl .staggerTo(this.menuItems, 1, { cycle: { x: ["56%","64%", "72%", "80%", "88%"] }, ease: Power2.easeInOut }) .reverse(); }; render() { return ( <div className="main"> <button onClick={this.toggleMenu} id='toggleMenu'> menu </button> <div className="projects" ref="projects"> {Data.projects.map((project, index) => { return ( <div className="project" ref={ project => this.menuItems.push(project)} key={index} id={`project-${index + 1}`} onClick={this.togglePreviewCase.bind(null, index)} > <span> {project.name} </span> </div> ); })} </div> </div> ); } } This is the basic setup to render the DOM elements. As you can see I attached two properties to the component, the menu timeline and a menu items array: // menu items this.menuItems = []; // menu tween this.menuTl = new TimelineLite({paused:true}); Also I added the menu items to the array using the ref method on each menu item: <div className="project" ref={ project => this.menuItems.push(project)} key={index} id={`project-${index + 1}`} onClick={this.togglePreviewCase.bind(null, index)} > <span> {project.name} </span> </div> The idea behind using an array is that GSAP can use them without any problems, weather is to create a stagger animation or a common animation for all the elements. Since you're using the cycle property for each element there's no need to use a stagger method, just a regular to() instance that animates all the elements at the same time. The rest is pretty much self explanatory and we've been through that in a different topic. The main idea, as mentioned in my previous post, was adding a property in the state to track if the menu is expanded and if there's a menu panel expanded as well (menuExpanded, targetPanel). Then is the toggle method, we'll go into the basic stuff of this and then get back to it: toggleMenu = e => { const { menuExpanded, targetPanel } = this.state; // if a panel is expanded, collapse any panel and then reverse // the menu timeline if ( targetPanel !== null ) { return TweenLite.to( this.menuItems.slice(0, targetPanel + 1), 1, { x: '+=50%', ease: Power2.easeInOut, onComplete: () =>{ // toggle the state property this.setState({ menuExpanded: !menuExpanded, targetPanel: null }); this.menuTl.reversed( menuExpanded ); }// on complete });// tweenlite instance } // toggle the state property this.setState({ menuExpanded: !menuExpanded }); this.menuTl.reversed( menuExpanded ); }; The basic stuff are the final two lines, basically the state property is being updated and we're using that to trigger the animation in order to keep consitency between react and the timeline reversed state. I'm using the reversed property because the timeline is paused in the constructor and when is created instead of playing it is reversed, with that we can toggle the reversed property of the timeline to toggle it's direction. Then the part of each panel animation: togglePreviewCase = (index, e) => { const { menuItems } = this; const { targetPanel } = this.state; // create two arrays with the elements that should be animated // one for the elements that will be expanded and the ones that // will be collapsed let expandArray, collapseArray; // if the current target is null means no element is expanded if ( targetPanel === null ) { // only create an array for the elements that will be expanded expandArray = menuItems.slice(0, index + 1); TweenLite.to( expandArray, 1, { x: '-=50%', ease: Power2.easeInOut }) } else if ( index < targetPanel ) { // the new target is already expanded, we have to collapse // the panels before that, there's no expand animation collapseArray = menuItems.slice(index + 1, targetPanel + 1); TweenLite.to( collapseArray, 1, { x: '+=50%', ease: Power2.easeInOut }); } else if ( index > targetPanel ) { // the new target is not expanded, we have to expand all the // elements between the previous target and the new one expandArray = menuItems.slice(targetPanel + 1, index + 1); TweenLite.to( expandArray, 1, { x: '-=50%', ease: Power2.easeInOut }); } else if ( index === targetPanel ) { // the current target element is being clicked, reverse that TweenLite.to( menuItems[index], 1, { x: '+=50%', ease: Power2.easeInOut }); // the new target index should be the previous element if the // index is bigger than 0 return this.setState({ targetPanel: targetPanel > 0 ? targetPanel - 1 : null }); } // set the current index as the target panel this.setState({ targetPanel: index }); }; The comments and the explanation for the use of arrays pretty much cover what's happening here. The idea is to keep track of the index of the element being clicked to create an array of elements that should be animated. Then depending on the index position of the target panel (where the user clicks) if those will be collapsed or expanded. Finally the other part of the toggle menu code. If the user clicks on the toggle menu button and there's a menu panel expanded, we create a tween for all the expanded panels and when that's complete, reverse the menu tween and set the target panel to null using an onComplete callback. Happy Tweening!!
  3. Hi @tomsah, Unfortunately I got caught up in a project now and haven't been able to follow up on your slide menu project. I'll try to whip something tomorrow to try to help you. For the moment what you should do is add to your menu component a property in the state in order to know if any panel is expanded or not and if the menu is being collapsed, ie, the user wants to hide the menu. The idea is this: when the menu button is clicked check that state and if a panel is open reverse the panels animation, then using a onReverseComplete instance check if the idea is to hide the menu or just collapse the panels. If the idea is to hide the menu then reverse the menu animation, if not do nothing. Happy Tweening!!!
  4. Animating a card game in React with GSAP

    Yep, the principle remains the framework changes, that's all. Is just about the "right moment" to update the DOM in cases like this. How to do that is up to whatever framework is being used.
  5. Animating a card game in React with GSAP

    @OSUblake has a great point regarding DOM reconciliation. Keep in mind that in react DOM manipulation is normally considered a no-no, anti-pattern, no-op and all those fancy terms that exists to politely tell you: "DON'T DO THAT!!!" I'd try to avoid changing the elements from one parent to another, but it this is a must-do situation using an onComplete callback (you see how shamelessly I'm re-using Blake's ideas?? ) to update the state, perhaps the index position of each child to match the order in which each parent is added to the DOM, to place them in the corresponding parent, would be a good way to do it. Perhaps using redux or mobx to store everything and access throughout the app. Happy Tweening!!
  6. Animated Page Transitions in React using GSAP

    Hi, Honestly going through all the motions to teach you how to work with redux is out of my capacities in terms of the required time and where exactly you are right now in terms of your coding knowledge. Redux basically let's you rescind, as much as possible, of using component's states in order to keep reference and track of different properties that might change during the app's life cycle. Before redux (and also is worth mentioning it's predecessors Flux and ReFlux) and mobx, a React app had references in the states of different components. The issue was that when you needed to access the state of a sibling component all hell was unleashed, because you needed a method from the parent component to be called in Child A in order to pass that property to the parent and then to Child B. Of course the deeper a component was in the app's tree, the bigger the unleashed hell. It was coding nightmare. Enter redux. Redux is just an object (store) with all the properties you want to keep track and it's accessible via the connect high order function from react redux in all the components of the app that you need. The point is that in order to add and update the property in the store you need actions and actions creators. Those are added to the component's props so they can be accessed and used easily. I strongly recommend you to go through the egghead course (I believe is free) and redux's official docs in order to get a better grasp of how to work with redux and react, is really not that complex. Regarding the error is fairly simple. You're trying to render something that comes as undefined. Similar to this: class Route extends Component { render(){ return <div> // some component here, most likely a function is returning undefined </div>; } } In the comment I mention a function, but also could be a ternary operator or some iteration of a map array helper. Without the entire code and the data structure is impossible to tell. A common scenario is that, since the render method can be called more than once as the state and props are updated (mostly during async code) an array could be empty or something else could be coming as undefined, therefore passing that to the render method which ultimately returns that error. You can use chrome's debugger to put a break point in that render method in order to check the call stack and the scope or use console.log() in the render method, before the return statement of course: class Route extends Component { render(){ console.log(/*check props and state here in order to track what could be returning as undefined*/); return <div> // some component here, most likely a function is returning undefined </div>; } } Happy tweening!!!
  7. Animated Page Transitions in React using GSAP

    Ahh, sorry missing closing parenthesis there. It should be: <button onClick={this.buttonClickHandler.bind(null, '/')}>Home</button> By the actions path I mean, that since you're using redux you have actions and action creators. Normally in small projects you put all your actions and action creators in one file and export each one as a named export. In larger projects people like to ether put each component, it's own reducer and actions in separate folders, or create a components folder, an actions folders and a reducers folder to place each components actions and reducers and export them via an index.js file. I'm assuming that you have some degree of experience working with redux and react-redux. If you're not very familiar with it you can check this resources: https://redux.js.org/faq/code-structure https://medium.com/front-end-hacking/the-three-pigs-how-to-structure-react-redux-application-67f5e3c68392 https://marmelab.com/blog/2015/12/17/react-directory-structure.html https://egghead.io/courses/getting-started-with-redux The egghead course is by Dan Abramov himself (the creator of redux). He goes kind of fast but the concepts are very clear though. Happy Tweening!!!
  8. Animated Page Transitions in React using GSAP

    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!!!
  9. multiple slide panel GSAP & React

    Hi, Does this comes close to what you're trying to do?
  10. multiple slide panel GSAP & React

    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!!!
  11. Importing TweenMax into React component

    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!!
  12. Animate out - React

    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!!
  13. Simple React/GSAP animation

    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!!
  14. Animate out - React

    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!!!
  15. Animate out - React

    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!!!