Jump to content
Search Community

UnioninDesign

Members
  • Posts

    21
  • Joined

  • Last visited

Contact Methods

Profile Information

  • Location
    Denver

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

UnioninDesign's Achievements

10

Reputation

  1. Follow up question...how do I capture the instance of the <li> element itself? What I'm trying to do...since these cards showcase quite a bit of data...is to add additional animations to the card or <li> itself if certain conditions are met. The data-attributes method works great for spotting a certain value etc, but then triggering another animation doesn't necessarily make it clear why a burger, or a whale in my case, is flying across the screen? From my research (and errors!) you can't tween a data-attribute, nor is it possible to render a data attribute (like take the value from the attribute and pass it to a <h2> tag or something?). I suspect that, because I had already used .map to go through and animate the entire array with staggerFromTo I may be complicating things? Perhaps a for each loop is in order? Any ideas are appreciated!
  2. HI friends - I noticed a bug and wanted to post a quick update in case anyone is working on a similar project. I never got any errors, but noticed that sometimes my animation would fire when it wasn't supposed to? I have two theories as to why this happened, and wanted to share my fix: From my research debugging this issue, it sounds like data-attributes may not be the most stable? As I am going through the data using .map(), and then checking data-attributes for a certain value....but also repeating my timeline until the next data fetch, I believe the function was being called again when it hit the same index or position in the array where it had been called previously, regardless of the value? My console logs did indeed confirm that this was from a previous booking! So here's what I did, with snippet below: After each time the 'whale' animation, or in this example burgerFlip, use setAttribute to add a new data-attribute in the conditional logic, add a check using the hasAttribute() method to see if the new attribute was present before calling the function. I haven't updated the codepen yet, but it would look something like this: burgerFlip(instance){ const dataValue = instance.target.getAttribute("data-value"); //check if the instance had called the burgerFlip previously... const prevBurger = instance.target.hasAttribute("data-burgerflipper"); //returns true or false if ( dataValue > 10 && prevBurger == false ){ TweenLite.fromTo(".burger", 3, {x: -250, autoAlpha:0, rotation: 0}, {autoAlpha:1, rotation:720, x:250}) //add a data-attribute to the instance that had called the burgerFlip function... instance.target.setAttribute("data-burgerflipper", "_self"); //per MDN docs you need to add "_self" to specify the instance in question or you get an error } else { console.log("sorry no burger"); } } Thanks for the help and support as always!
  3. As always - thanks @Rodrigo - works great! Adding the data-attribute to my list items totally makes sense - but glad I made this post, I don't think I would have figured out the 'instance.target.getAttribute' part on my own!
  4. Hi team! I've been struggling for a while to get some conditional logic in place with my animations. Some quick backstory to give my codepen some context...Anyone who's curious can also see my two other threads, all related to the same project, but I thought I would make a new one with a greatly reduced example. There are LOTS of other questions about animating list items, but not many with react? And none that I've seen that involve potentially large amounts of data, or conditional logic mixed into the timeline? Backstory: I've built an interactive USA map using D3/react-simple-maps, it shows all of my companies bookings on a 5 minute delay...usually close to 50 at a time...100-150 an hour, about 8,000 in a day! That's a lot dots to show on a map! Thanks GSAP for your awesome timeline and stagger methods to make it more digestible. So...enter the 'ticker'... I decided in addition to the map I would add a sidebar that has a card with data on each booking, like who it is, the dollar amount etc. These cards appear in the ticker at the same time dots appear on the map. Now that we're live, everyone wants new features. "Wouldn't it be cool if there was a booking over a certain dollar amount, we could show a whale fly across the screen?" How do I that???? A couple of things, then some code! - Because these ticker cards contain lots of data, I do want them to pause in place for long enough that someone could read them if they wanted...after digging through forums and lots of codepen examples, the best I could come up with was multiple staggerFromTos chained together! It's pretty choppy but it works...ish. The one that is live has better timing than the the codepen! Also - no map in this example - just list items...and a cheeseburger callback! - I've been playing around with .add and .call, or adding a label somewhere to my timeline - not quite there yet! - Nothing I've done actually considers the data! In my codepen, I've created an array with id, and values, and I'm hoping if a card reaches the top of the box (or anywhere in the box) and has a value greater than 10, it will call the cheesburger animation to do its thing? Any ideas? what am I doing wrong? Where should this conditional logic live - within the function, or outside of the timeline somewhere...possible in componentDidMount or DidUpdate? - I'm still getting the hang of react and its ways...but our best practice is to use keys when going through an array with the .map() method, and use createRefs to set targets for the animation. Is there a way I can access a particular card by it's key or ref etc? https://stackblitz.com/edit/react-iiesig?embed=1&amp;file=index.js Thanks!
  5. I have a follow-up question, and I've been trolling through the forums and stack overflow in search of a solution that still escapes me at the moment! So...quick recap...I am animating markers on a map of the US, and since there's over 500 an hour, roughly 8,000 a day the stagger methods and TimelineMax have made a very nice visualization - thanks GSAP! -- How to Toggle 'visibility' of tweens throughout the timeline -- The question is that, to avoid showing all of these map markers on render, I've set the CSS for the svg circle element to " visibility: hidden", which I then toggle in the animation using autoAlpha: 1. If I drag the slider to the left to go back in time, or click the reverse or restart buttons, and also when the animation repeats...all of the map markers have now been set to autoAlpha: 1, and every single one that has rendered so far is visible. Without a better codepen (Still working on the Globe...) and, it looks likes GSAP has many built-in methods to do this, but I haven't found the right one? I've tried chaining to my buttons something like this.tl.restart().pause(0)... as well as clear(), kill() - there's also remove(), Invalidate() - so many options! I must not be using any of them correctly! I've also attempted to use many of these in the onCompleteAll callback that fires at the end of the timeline, still no success! Any ideas? Additionally - and this relates more to react/javascript/dealing with async data fetching...but when the new data set comes in I'm having trouble making a smooth transition from one set of map markers to the next. I know...tough to guess without seeing what's going on, but if you have any tips on best practices or can point me towards examples of someone else doing this that would be huge help! I did do as Rodrigo suggests above and do a deep comparison of the two data sets using the loDash _.isEqual method, inside componentDidUpdate. I was hoping the 'this.forceUpdate() wouldn't be necessary but so far seems to be the only way to tell the D3 map library (react-simple-maps) that it's time to generate new markers? I'm also working on migrating this project into true D3, should be ready for iteration v2! Here's a snippet of that function in my child or map component where it is getting the data as state passed down as props: componentDidMount() { //Animations with GSAP this.animateBookings(); } componentDidUpdate(previousProps) { //console.log("DidUpdate child was called!"); if (!_.isEqual(previousProps.data, this.props.data)) { //console.log("DidUpdate child was called no. 2!"); this.setState({ data: this.props.data }); this.theWhale(); this.tl.kill(); this.animateBookings(); this.tl.play(); } } Thanks to everyone who has helped with this project! Big shout out to @Rodrigo !!!
  6. This was a tricky one for me that took a while to figure out...but Rodrigo was spot on in terms of what the problem was...when fetching data asynchronously, you need to make sure the fetch is complete before attempting to render other components. I was getting an error about 'Can’t call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.' In case anyone else is dealing with the same issue - this article here was a huge help! Although this is a React issue, and not GSAP - worth noting how to handle these kinds of things! https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component/
  7. Thanks Rodrigo! You've got the right idea...and it was getting late...I was missing (duh) the . in front of the className "animateMapMarker" which should have been ".animateMapMarker", like you said GSAP was getting a string for the target and not the element with a className of ".animateMapMarker". And yes, having added the . it kind of works...There's a method in the map library that will move the 'center' of the map and rotate the globe, so I do need to write some more code to get it to a point where, when I call the staggerFrom method, the map markers get bigger (which works) AND that marker would then be displayed front and center! If I could get a bezier line to animate and connect the dots, this could be cool app that would track movements around the globe with an interactive timeline...but it's not quite there yet. I'll see what I can do to use this example to recreate the problem I am having at work...I tried it again this morning, but how weird! Data that was hard-coded in an external file, then imported and passed to the 3rd party components would animate just fine using this same method of targetting the className of the <circle> element for the map marker. But then, the 'react-way' of fetching data higher up in the component tree, passing it down as props - I get markers but no animation? Puzzling and also frustrating! To whet your whistle, if you're curious I did reach out to the map library creators about a month ago concerning this issue, before I found the bandAid of adding the className as a target, which seemed like a good fix: https://github.com/zcreativelabs/react-simple-maps/issues/119 They did respond with this codesandbox: https://codesandbox.io/s/rzr73rjjq At the time my focus was a bit more on using something like React Transition group, and trying to wrap a new 3rd party component around the 3rd party Marker component was breaking its parent, the map projection itself. The codesandbox does exactly that with a different animation library, but the data is still hard-coded so I might still run into the same issue...I guess I'll find a way to hammer it into place? If you think of any other ideas let me know! I'll of course update the thread with any breakthroughs, and hopefully a better working example so you can see what's going on!
  8. Hello again! I took some time to make a test case to play around with...getting stuck on a few things... Can we talk about use of createRef, callback refs etc? I noticed in the example link you sent, your target looks like this: div className="box" ref={e => this.box = e}> <span> MOVE & ROTATE </span> </div> I tried to replicate something similar, but could be getting stuck with a 3rd party component library so I can play with maps (react-simple-maps, that is...) Here's kicker - I've had GSAP working for several weeks now with test data that was hard coded. When trying to make my app iterative, as discussed in the posts above, I'm trying to go through the best practices of fetching data in a parent component, passing state down as props to a child component - my data renders, but the animations are gone? Up until then, my animations were working successfully by adding a className to the html element (in this case, the svg circle tag inside the <Marker> 3rd party component) I've tried to recreate a similar use case, at the moment I'm not getting any animations to fire up? https://stackblitz.com/edit/react-globe-timeline?embed=1&amp;file=index.js Let me know if you have any suggestions...much appreciated!
  9. Thanks! For now I just added a simple function to log that the animation is complete, and the animation restarts as expected with no errors (because of the repeat: -1 passed in as params to this.tl = new TimelineMax(....). Always a nice when it's an easy fix! A quick note for posterity...depending on how you do this, especially with react, you may need to add 'this' in front of the onCompleteAll callback. Here's the WALL of code again: onCompleteAll() { console.log("Animation complete"); } updateSlider() { this.setState({ sliderValue: Math.round(this.tl.progress() * bookingData.length) }); } componentDidMount() { bookingInterval(); //animateBookings(); //Animations with GSAP this.tl = new TimelineMax({ onUpdate: this.updateSlider, repeat: -1 }); //controls time in seconds in between booking cycles var next = 3; //controls fadeout var nextOpacity = 5; var opacity0 = 10; var fadeOutDelay = 10; //how long markers and lines remain on screen prior to fadeout var fadeOutDuration = 3; //how long it takes bookings to fadeout this.tl .staggerFrom( ".animatedTick", 7, { ease: Power3.easeInOut, y: 1000, opacity: 1 }, next ) .staggerTo( ".custMarkers", 0.3, { autoAlpha: 1, ease: Elastic.easeOut.config(0.2, 1.5), opacity: 1, scale: 5, force3D: false, yoyo: true, repeat: 1 }, next, 1 ) .staggerTo( ".clientMarkers", 0.3, { autoAlpha: 1, ease: Elastic.easeOut.config(0.2, 1.5), opacity: 1, scale: 5, force3D: false, yoyo: true, repeat: 1 }, next, 1.5 ) .staggerTo( ".lineAmation", 1.5, { visibility: "visible", ease: Power3.easeOut, autoAlpha: 1, opacity: 1, strokeWidth: 2 }, next, 2 ) //FadeOut animations are chained to the original timeline, with a new set of animations for each target .staggerTo( ".animatedTick", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0, y: -200, x: 100, scale: 0, rotationY: -360, display: "none", ease: SlowMo.easeOut }, nextOpacity, opacity0, this.onCompleteAll ) .staggerTo( ".custMarkers", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, this.onCompleteAll ) .staggerTo( ".clientMarkers", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, this.onCompleteAll ) .staggerTo( ".lineAmation", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, this.onCompleteAll ); }
  10. I'm having the same issue using create-react-app, not sure if I made the same/similar error? I tried to move some things around in my staggerTo methods but still getting the error...any ideas? Not sure if this is a react-thing, or a google chrome thing, but I do suspect a syntax error? Per the timelineMax staggerTo docs, is it necessary to add an onCompleteAll callback, or some way to let the animations know when the animation is complete? I get this error at the end of the timeline, and sometimes during if I scrub too quickly through the timeline. I am using this for a data visualization so the timeline will be up to 15 minutes long! The error: So the note about speed optimization could suggest a browser issue? But I get the same thing in Firefox too. Here is my animation code, which lives in react's componentDidMount method - any suggestions are appreciated! updateSlider() { this.setState({ sliderValue: Math.round(this.tl.progress() * bookingData.length) }); } componentDidMount() { //Animations with GSAP this.tl = new TimelineMax({ onUpdate: this.updateSlider, repeat: -1 }); //controls time in seconds in between booking cycles var next = 3; //controls fadeout var nextOpacity = 5; var opacity0 = 10; var fadeOutDelay = 10; //how long markers and lines remain on screen prior to fadeout var fadeOutDuration = 3; //how long it takes bookings to fadeout this.tl .staggerFrom( ".animatedTick", 7, { ease: Power3.easeInOut, y: 1000, opacity: 1 }, next ) .staggerTo( ".custMarkers", 0.3, { autoAlpha: 1, ease: Elastic.easeOut.config(0.2, 1.5), opacity: 1, scale: 5, force3D: false, yoyo: true, repeat: 1 }, next, 1 ) .staggerTo( ".clientMarkers", 0.3, { autoAlpha: 1, ease: Elastic.easeOut.config(0.2, 1.5), opacity: 1, scale: 5, force3D: false, yoyo: true, repeat: 1 }, next, 1.5 ) .staggerTo( ".lineAmation", 1.5, { visibility: "visible", ease: Power3.easeOut, autoAlpha: 1, opacity: 1, strokeWidth: 2 }, next, 2 ) //FadeOut animations are chained to the original timeline, with a new set of animations for each target .staggerTo( ".animatedTick", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0, y: -200, x: 100, scale: 0, rotationY: -360, display: "none", ease: SlowMo.easeOut }, nextOpacity, opacity0 //2 ) .staggerTo( ".custMarkers", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, 1 ) .staggerTo( ".clientMarkers", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, 1.5 ) .staggerTo( ".lineAmation", fadeOutDuration, { delay: fadeOutDelay, autoAlpha: 0 }, nextOpacity, opacity0, 2 ); } To get an idea of the larger project, Here's two other threads I've initiated...sorry for being 'that guy' that does not post a codepen, but this is a large full stack application with a lot of moving parts. Reduced case codepen is coming soon, possibly this weekend! This one tells you a bit more what I'm up to - bigUps to Mikel! And in this one Rodrigo helped out big time getting the range slider working! Thanks all for the help!
  11. Just in case anyone reads this and wants to go a little deeper, I have a related question from a few weeks ago, similar use case:
  12. Ah ha - as we say in French - "le mot juste!" Rodrigo you sir are a Wizard! I got pretty close by the end of the day yesterday - I think it came down to missing a 'this' in front of my updateSlider callback! (it's a class...duh...). Now onto the next...I'm in awe of GSAP and the support from its community. There are some very exciting possibilities for making data visualizations here! My apologies again that I have not supplied any codepen or reduced case examples...since this is a worky-thing it's not very public...but I will try to replicate something with a more public dataset and will update this post...maybe this weekend? If anyone is interested in building interactive maps using D3 (in this case react-simple-maps) along with React, and a robust animation library like GSAP to create an interactive timeline, this is the place to be! Thanks again!
  13. Quick update...so here's a snip of the callback function: function updateSlider(context) { console.log("State? ", context.state); let currentProg = context.state.value; //context.setState({ value: currentProg + 1 }); context.tl.progress(currentProg / bookingData.length); } And I'm calling it at the timeline declaration at so, which I believe is correct: this.tl = new TimelineMax({ onUpdate: updateSlider, onUpdateParams: "{self}" }); But...because all of the animations live inside componentDidMount(){}, if I pass this in at after the timeline, inside componentDidMount...we are not getting passed the value that has been set here inside these two slider methods: onSliderChange = value => { this.setState({ value }); console.log("Value is: ", value); }; onAfterChange = value => { console.log(value); this.tl.progress(value / bookingData.length); this.tl.resume(); }; ...It will continue to update with no value and error out after a few seconds? Alternatively, passing in the updateSlider callback right after onSliderChange and onAfterChange, just above componentDidMount, will give you an undefined error (makes sense). I think this is not a GSAP problem! but something to do with the architecture or my app! Any ideas are appreciated! Sooooo close!
  14. Rodrigo - the man I was hoping to hear from! I've spent LOTS of time in this forum the past week or so, and your posts are always very helpful! Issue fixed per below! But the next one at the bottom of this post - how do we get the slider to update the position based on the current value of the animation? BTW - Good eye spotting an inconsistency in my code! Easy to do when you try things a bunch of different ways...however, when setting a range of 0 to 100, or 0 to bookingData.length (which indeed gives you a value for how long the dataset is...I'll stick with that!)....the same result was that it kills the animation, but not getting any errors in the console. You could continue to drag the slider around and it will keep logging the values, but the play, pause, resume don't function, and you need to refresh the page to get the animation to run again. After a few more minutes playing around...as I'm pretty sure I had tried what you had suggested, but with no luck...I noticed some code I had copied into to the top of the render method: render() { this.tl .kill() .clear() .pause(0); return ( The fix was to remove the .kill() and .clear() methods!!!!! Although I'm a little curious why these are here and what purpose they might serve, perhaps to pass in some kind of initial state to the playback methods at rendering...which is not best practice, correct? better to set the initial state in your constructor, or even to use a parent level component to manage the state and pass it down as props? Still have lots to learn! For posterity, scrolling on the timeline now brings you to the next value (or booking!), but it does help to pass in the .resume() method to get the timeline working automatically, if not it will be pause when onAfterChange talks to the slider: onAfterChange = value => { console.log(value); this.tl.progress(value / bookingData.length); this.tl.resume(); }; So the next challenge, which I hope to figure out soon...we need the range slider to update itself as the timeline progresses. It sounds like what we need is an onUpdate callback: onSliderChange = value => { this.setState({ value }); console.log("Value is: ", value); }; onAfterChange = value => { console.log(value); this.tl.progress(value / bookingData.length); this.tl.resume(); }; updateSlider = () => { let currentProg = this.state.value; this.tl.progress(currentProg * bookingData.length); console.log("running", currentProg); }; I keep getting an error that on updateSlider is undefined? I am trying to pass it in like so. Quick note that all of my animations are currently housed in componentDidMount, but I do need to move these to an external animation.js file... onSliderChange = value => { this.setState({ value }); console.log("Value is: ", value); }; onAfterChange = value => { console.log(value); this.tl.progress(value / bookingData.length); this.tl.resume(); }; updateSlider = () => { let currentProg = this.state.value; this.tl.progress(currentProg * bookingData.length); console.log("running", currentProg); }; componentDidMount() { bookingInterval(); //Animations with GSAP this.tl = new TimelineMax(); // parameter: { "onUpdate", updateSlider } var next = 3; this.tl .staggerFrom( ".animatedTick", 3, { rotationY: -180, ease: SlowMo.easeOut, y: 700, delay: 1, scale: 2, opacity: 0 }, 3, "nextBooking -=3" ) .staggerFrom( ".custMarkers", 1.5, { cycle: { x: [-1000, 1000], y: [-1000, 1000] }, bezier: { curviness: 1, values: [{ x: 0, y: 0 }, { x: 125, y: -80 }, { x: 250, y: 0 }] }, ease: Power1.easeOut, opacity: 0, scale: 10 }, next, 1 ) .staggerFrom( ".clientMarkers", 1.5, { cycle: { x: [-1000, 1000], y: [-1000, 1000] }, bezier: { curviness: 1, values: [{ x: 0, y: 0 }, { x: 125, y: -80 }, { x: 250, y: 0 }] }, ease: Power1.easeOut, opacity: 0, scale: 7 }, next, 1.5 ) .staggerFrom( ".lineAmation", 2.1, { ease: Sine.easeIn, opacity: 0 }, next, 2, "-=1" ); } render() { this.tl.pause(0); return (............ So up next, where do I pass in this callback? Thanks a ton!
  15. I really appreciate the help on this! I did get the sequence timed out the way I want, but had a follow up questions on callbacks in case either of you two gentleman would like to kick this can a little further down the road? Case: so we're building a map, and using this animation I'll get report every 15 minutes with 100 - 500 bookings to display! Now that I've got a nice stagger effect so you don't see all the bookings rendered simultaneously, I'm noticing that the map starts to get cluttered? I'd like to write some kind of callback (if necessary) to make the bookings fade out after 5-7 seconds ish? I think I'm close but haven't quite nailed it just yet Here's my animation code - with some parts adapted from your codepens. I also decided to add a sidebar that show text about in each booking so it's not just a bunch of dots on the screen - the target class ".animatedTick" leads off the timeline: this.tl = new TimelineMax(); // parameter: { onUpdate: updateSlider } var next = 3; var bookingDuration = 6; this.tl .staggerFrom( ".animatedTick", 3, { rotationY: -180, ease: SlowMo.easeOut, y: 700, delay: 1, scale: 2, opacity: 0, autoAlpha: 1, timeResolution: bookingDuration }, 3, "nextBooking -=3" ) .staggerFrom( ".custMarkers", 1.5, { cycle: { x: [-1000, 1000], y: [-1000, 1000] }, bezier: { curviness: 1, values: [{ x: 0, y: 0 }, { x: 125, y: -80 }, { x: 250, y: 0 }] }, ease: Power1.easeOut, opacity: 0, scale: 10, autoAlpha: 1, timeResolution: bookingDuration }, next, 1 ) .staggerFrom( ".clientMarkers", 1.5, { cycle: { x: [-1000, 1000], y: [-1000, 1000] }, bezier: { curviness: 1, values: [{ x: 0, y: 0 }, { x: 125, y: -80 }, { x: 250, y: 0 }] }, ease: Power1.easeOut, opacity: 0, scale: 7, autoAlpha: 1, timeResolution: bookingDuration }, next, 1.5 ) .staggerFrom( ".lineAmation", 2.1, { ease: Sine.easeIn, opacity: 0, autoAlpha: 1, timeResolution: bookingDuration }, next, 2, "-=1" ) I've tried chaining to the end of this with a few different methods, am I close? This one gives me an error 'delayed call is not a function' - should this be passed in as params to the timeline? I have this one chained right after the last staggerFrom... .delayedCall(bookingDuration, bookingFadeout); function bookingFadeout() { this.tl.to( [".animatedTick", ".custMarkers", ".clientMarkers", ".lineAmation"], bookingDuration, { autoAlpha: 0, opacity: 0 } ); } And i also tried to chain this to end of the sequence... .to( [".animatedTick", ".custMarkers", ".clientMarkers", ".lineAmation"], 5, { opacity: 0, ease: Power1.easeInOut, autoAlpha: 0 } Which does nothing? Another idea was to find a way to go by the number of repeats, but the onRepeat() method executes after each repeat, not say, every 6, 7 or 8 repeats? I have a few things going on here, like trying to set the timeResolution in each staggerFrom tween equal to a global variable called 'bookingDuration' - also not working... Any ideas are appreciated!
×
×
  • Create New...