Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...

Rodrigo last won the day on March 17 2019

Rodrigo had the most liked content!

Rodrigo

Moderators
  • Posts

    1,768
  • Joined

  • Last visited

  • Days Won

    158

Posts posted by Rodrigo

  1. Hi,

     

    Yeah it is by design. A stagger method in a timeline returns a TimelineLite instance with a TweenLite instance for each element in the array passed to it, and since getLabelsArray is a method of the TimelineMax constructor you're getting undefined.

     

    Also keep in mind that, besides the parameters of each stagger tween passed in the stagger method, you have no further control over those instances created by these methods. If you want to place a label where each stagger animation starts, you might want to take a longer road and use a loop in order to create each individual tween and place the desired label inside a TimelineMax instance.

     

    The current code you posted, only gives you the possibility to add a label when the first stagger tween created by each stagger method starts.

     

    Beyond the fact that:

     

    console.log( children[i].getLabelsArray ); // -> undefined

     

    There is not much I can infer from that code. Perhaps you can provide a few more details in order to get a better grasp of the roadblock you stumbled in your code.

     

    Happy Tweening!!

    • Like 3
  2. Hi,

     

    Well, there are a few ways to tackle this. The ideal react-way should be to keep track of the current card's index position in the array and check that value in order to update the component's state with the card's value, and if the updated state is bigger than 10, run the animation. Unfortunately your code is not structured like that and honestly I don't see the need to refactor everything if the only side effect of an animation update is trigger another animation and not data or DOM updates.

     

    With that in mind the solution is to mess around with scope and this, which until this day and after working in JS for so long it can come up as an obscure and difficult topic. Lucky for us GSAP does helps a lot in terms of binding and managing scope.

     

    First in the render method add an extra attribute to the DOM element created in the JSX:

    <li className="dataCard" key={id} ref={e => (this.listItem[id] = e)} data-value={listItem.value}>
      <p>ID: <span>{listItem.id}</span></p>
      <p>Value: <span>{listItem.value}</span></p>
    </li>

     

    As you can see I'm passing the data-value attribute which is the value you want to check.

     

    Then in the GSAP part of your code you have to use an onComplete callback inside the configuration of the stagger method, not using the onCompleteAll callback:

     

    animateDataCards(){
      this.tl = new TimelineLite({repeat: -1 });
      const duration = 1;
      const next = 2;
      this.tl
      .staggerFromTo(this.listItem, duration, {autoAlpha: 0, y:500},{autoAlpha: 1, y:275}, next)
      .staggerFromTo(this.listItem, duration, {y:275}, {y:125}, next, 2.5)
      .staggerFromTo(this.listItem, duration, {y:125}, { y:10, onComplete: this.burgerFlip, onCompleteParams: ["{self}"] }, next, 2.5)
      .staggerFromTo(this.listItem, 1, {y:10},{ autoAlpha: 0, y: -250, ease: Power3.easeInOut },5, this.onCompleteAll)
    }

     

    Now the key here are two GSAP specific concepts. The first concept is the fact that any stagger method returns an array of GSAP instances, so each loop in the array passed to the stagger method, generates a specific TweenLite method in this case. Because of that we can attach a callback on each of those. The second, and I'm sure you already saw it, is the "{self}" string passed as the parameter for that specific callback. That tells GSAP to pass the instance as the first parameter in the callback.

     

    Then in the burger flip callback you can run this code:

    burgerFlip(instance){ 
      const dataValue = instance.target.getAttribute("data-value");
      if ( dataValue > 10 ){
        TweenLite.fromTo(".burger", 3, {x: -250, autoAlpha:0, rotation: 0}, {autoAlpha:1, rotation:720, x:250})
      } else {
        console.log("sorry no burger");
      }
    }

     

    Here is the kicker, each GSAP instance has a target property, which is a reference of the DOM node being animated, hence instance.target in the code. Since we attached a data-value attribute to the element, we can now check that value and run our conditional logic and see if the burger will animate or not:

     

    https://stackblitz.com/edit/react-gsap-burger-conditional

     

    Happy Tweening!!

    • Like 4
  3. Indeed this is happening since the latest VSCode update and is more likely a VSCode thing and how intellisense (not too intelligent it seems? ;)) is working.

     

    One solution could be to define global variables in your workspace or change the settings. Much like you I've seen this behaviour when working with VSCode, lucky me, in the project I'm working right now I'm using webstorm, so I don't have to deal with this for the next weeks :D, but still you could start an issue in VSCode repo or look in stack overflow.

    • Like 3
    • Thanks 1
  4. 2 hours ago, UnioninDesign said:

    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?

    Let me see if I can follow this through. You hide your SVG elements using CSS, then using autoAlpha you set them to be visible. Finally you want them to be hidden if you reverse the timeline or restart it. Is that right? Normally restarting the timeline should be enough, since GSAP records the starting values of the instance's targets. This over-simplified example illustrates that. The restart button restarts the timeline and the reset progress button set's the progress to zero and then plays the timeline:

     

    See the Pen aMvzpx by rhernando (@rhernando) on CodePen

     

    As for the transition between two sets of markers, yeah that's not the easiest task. Before setting the new state there are a couple of considerations. FIrst and most important, is the new set completely different from the previous or it could contain elements from the previous set? If the sets are completely different, then animate out the previous set, when that animation is complete update the state and animate in the new set. If the sets data intersects, then you'll have to filter the existing set, establish the elements that are not present in the new set. Then establish the elements in the new set that are not in the previous set. Animate out the elements in the previous set that are not in the new set, animate in the elements in the new set that are not present in the previous set. Of course in this whole process you need to update the state at the start and figure a way for the elements to be hidden when mounted. Another solution is to use React Transition Group to animate the mounting/unmounting of an element. Here you can see a sample of that:

     

    https://stackblitz.com/edit/gsap-react-simple-transition-group?file=simple-transition.js

     

    Hopefully this helps in some way.

     

    Happy Tweening!!!

    • Like 1
  5. Allright, this was not a fun morning activity :D

     

    We're meeting once again with this issue:

     

    https://github.com/facebook/react/issues/13654

     

    That issue stems from this particular thread in the forums:

     

    Now as you can see in this comment by Dan Abramov (from the React team) that this behaviour was change EXCEPT for portals:

     

    https://github.com/facebook/react/issues/13654#issuecomment-435150900

     

    Adding this makes it work on your case:

     

    Draggable.create(this.dragSchedule, {
      type: "x",
      edgeResistance: 0.75,
      lockAxis: true,
      allowNativeTouchScrolling: false,
      dragResistance: 0.3,
      throwProps: true,
      snap: this.snapX,
      onClick: this.wasClicked,
      zIndexBoost: true,
      autoScroll: 1.5,
      dragClickables: false,
      bounds: window
    });

     

    Why you may ask, because now the portal has the no-op handler associated to the click event so we need to counter that by doing the opposite of the previous behaviour:

     

    TrdOeRN.jpg

     

    hoHIQKZ.jpg

     

    Still I'll summon our beloved master @GreenSock in order to see if He has something to add to this, but I believe you landed in an edge use case.

     

    Anyway, setting the dragClickables to false seems to fix the issue.

     

    Happy tweening!!

    • Like 1
  6. I can see some shaking on Chrome in Win7 64.

     

    This basically has to do with the fact that your page is using a lot of CPU cycles probably for a lot of calculations.

     

    There are two aspects that caught my attention.

     

    One, you have this PNG file http://yasserzakaria.com/sauditimeline/images/tlBackground.png that is transparent. Transparent PNG files, specially big ones, can be very expensive. Try without it and see how it goes.

     

    Two, you are moving this element:

    <div class="tl-eras-container" style="width: 22500px; transform: translate(27.3895%, 0%) matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);">
      <div class="era" style="flex-basis: 2250px;"></div>
      <div class="era" style="flex-basis: 1750px;"></div>
      <div class="era" style="flex-basis: 3500px;"></div>
      <div class="era" style="flex-basis: 15000px;"></div>
    </div>

     

    That's 22500 px width, that's huge. This element is being constantly animated. Also you're animating another 22500px width element at the same time and finally you're animating a 2250px width element. All elements are being animated at the same time and that's causing a lot of calculations and repaints, and that's stressing a lot the CPU. In a smaller screen size performance is better.

     

    In terms of performance some layer updates are taking up to 35 milliseconds and the fps are dropping to 16 per second, that's definitely not good.

     

    My advice would be to try to move the card elements and not the entire wrappers/containers, especially that big, that could reduce the amount of calculations the CPU has to make. Also see if you can include either canvas or WebGL into the app, in order to improve performance.

     

    Finally, regardless of the performance issues, that is one good looking site, very appealing, intuitive, easy and fun to use, nice job!!!

     

    Happy Tweening!!

    • Like 4
  7. Hi and welcome to the GreenSock forums.

     

    What you can do is kill any previous animation affecting the wiggle target in the button click event and then create a new tween or even more simple, create a GSAP instance outside the click handler scope and restart it on every click:

     

    const wiggleTween = TweenLite.to(box, 0.5, {
      x: 5,
      ease: "myWiggle",
      paused: true
    });
    
    myButton.addEventListener("click", function() {
      wiggleTween.restart();
    });

     

    See the Pen VgJBpb?editors=0010 by rhernando (@rhernando) on CodePen

     

    As you can see in the live sample I'm using the second approach and regardless of how many times you click on the button the animation runs it's regular course, soto speak.

     

    Happy Tweening!!

    • Like 4
  8. Hi and welcome to the GreenSock forums.

     

    Is not quite clear what exactly you want to do, but I'm guessing that you want to update the opacity values of some instances after you have created the timeline? If that's the case the recommendation is to create a function that builds the timeline and when you call that function set the values you want:

     

    var animation;
    var myCondition = true;
    
    function createTimeline () {
      animation = new TimelineMax()
      	.to(".O",.5,{opacity: myCondition ? 0 : 1})
    	.from(".create-r",.5,{y:100, opacity:  myCondition ? 0 : 1});
    };

     

    Also if you can, please provide a reduced sample illustrating what your issue is. Take a look at the post and video @Carl made for that purpose:

     

     

    Happy Tweening!!

    • Like 2
  9. In that case you could try forcing he images elements to a opacity of 0 using the config object available in the .tween methods of TimelineMax. The sweet part of it is that GSAP has a killer overwrite manager that will detect any other lingering GSAP instance that affects the images, kill them and send them to garbage collection.

     

    You could try something like this:

     

    tl.tweenFromTo(t, "b", {
      onStart: function() {
        TweenMax.to(images, 0.1, {opacity: 0});
      }
    });

     

    That will tween the opacity to 0 very quickly on all the images when the timeline playhead starts moving to the "b" label.

     

    Happy Tweening!!

    • Like 1
  10. Hi and welcome to the GreenSock forums.

     

    Actually you nailed it. Since you're adding the element to the DOM on a specific event, that event is the perfect moment to either play/unpause an existing GSAP instance targeting that particular element, or to create a live animation (that is not paused) targeting the element.

     

    Now in terms of performance, creating the GSAP instances, that is running the code that makes each Tween/Timeline is not expensive at all, unless you create thousands of them. If you run into a performance issue it most likely will be the rendering part of the animations.

     

    Finally keep in mind that in javascript creating a variable, adds a reference to a specific reference to a place in the device's memory while creating the TweenLite instance on the fly, without storing it will save some memory, again a tiny amount of bits, not much. On the same subject GSAP has a very good and reliable way of sending instances to garbage collection so there is a guarantee that there will be no memory leaks in the GSAP side of the app.

     

    Hopefully this sheds some light into your scenario ad helps you.

     

    Happy Tweening!!!

    • Like 2
  11. Can you make a regular codepen instead of a project?

     

    I took all the code and dependencies from the link you provided and made this codepen:

     

    See the Pen KJYvwW by rhernando (@rhernando) on CodePen

     

    But is not showing anything, since I don't know exactly what it should be doing. That will definitely help since the error you're reporting is not being thrown in the regular codepen, so that is a step in the right direction ;)

  12. Hi and welcome to the GreenSock forums.

     

    I'm not a big fan of testing this type of stuff in React, therefore I'm not in any way an expert in using Enzyme. I know my way around Jest, but I believe the issue is coming from the enzyme side of it.

     

    Right now I'm using React Testing Library for testing React apps and components.

     

    As I said I don't know how Enzyme works in terms of checking DOM nodes being present in the rendered side of the testing but basically when that specific method is running this.searchInput is null, whatever that is in your component. I assume that is pointing to a ref in the JSX part of your code and you're creating a reference in the component's constructor, something like this (an extremely over-simplified version of your component of course):

     

    class MyComponent extends Component {
      constructor(props){
        super(props)
        this.searchInput = null;
      }
      
      render(){
        return <div>
          <input ref={input => this.searchInput = input} type="text" />
        </div>;
    }

     

    If I was you I'd start by checking the component's ref object in order what's in there when running the tests and see how Enzyme deals with mocking and creating some sort of DOM representation when running the tests in order to do the tests and assertions to it.

     

    Finally I'd recommend going to Reactiflux's discord app and see the testing channel in order to get some support on how to proceed in this particular case and check what enzyme is putting there.

     

    When you have more insight on it, please report back, it will certainly be helpful for other users in the future.

     

    Happy Tweening!!

    • Like 1
  13. Well I don't know what to tell you, that code is working, but something you're doing is generating the error.

     

    Are you sure you have the scramble text plugin in your setup? Are you importing it or adding it to the HTML file using a script tag? It seems that could be the source of the error, since is being returned from the CSS Plugin.

    • Like 1
  14. A coupple of things.

     

    First check that the component's refs object indeed has a lawn2019 property in it. You can create a console.log() call to inspect the entire $refs object. Right now GSAP is complaining that the element is null right now. Be sure that in your template you actually add the ref to a specific DOM element like this:

     

    <div ref={"lawn2019"}></div>

     

     

    Also there is no need to create and store a GSAP instance in memory if you're using it only in the mounted hook, in that case just create the Tweenlite instance right away:

     

    mounted: function () {
      TweenLite.to(this.$refs.lawn2019, 3, {
        scrambleText:"THIS IS NEW TEXT"
      });
    }

     

    • Like 1
  15. Ok, so I did some research on the subject and this whole typescript is quite beyond my area of knowledge. Lucky for me I got in touch with this awesome guy in Reactiflux discord app: Damien Erambert (Twitter - GitHub) and He pointed me in the right direction.

     

    Turns out that first of all you need to import the type definitions for gsap from this package:

     

    https://www.npmjs.com/package/@types/gsap

     

    And then define both the GSAP instance and the DOM ref as the return values of the GSAP instance and the ref.

     

    I've updated the stackblitz sample so you can check it.

     

    Happy Tweening!!

    • Like 4
×