Jump to content
Search Community

Consistent Speed With Various Distance

ericnguyen23 test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I'm attempting to animate something traveling across the left side of screen to right side and repeating infinitely. 

 

The only issue is, I'm setting the same animation speed but one animation is rendering faster than the other.

 

Two questions:

  1. With the way I've set it up, how can I ensure consistent speed?
  2. Is there a better way of setting up this animation? I feel it's a bit strange to split up the animations like I've done, but couldn't figure out a cleaner way.

 

thanks, I'm using React btw.

 

Extracting the GSAP stuff for those who want a quick look:

 

  constructor(props) {
    super(props);

    this.tl = new TimelineLite({
      onComplete: function() {
        this.restart();
      },
    });

    this.moped = null;
  }

  componentDidMount() {
    this.tl
      .from(this.moped, 2, {
        delay: 2,
      })
      .from(this.moped, 2, {
        x: '-400',
        ease: 'Linear.easeNone',
      })
      .to(this.moped, 2, {
        x: deviceWidth,
        ease: 'Linear.easeNone',
      });
  }

 

See the Pen vwORxr by ericnguyen23 (@ericnguyen23) on CodePen

Link to comment
Share on other sites

Hey Eric and welcome,

 

I recommend using GSAP's .fromTo method like this:

 

this.tl
  .fromTo(this.moped, 2, {
    x: '-400',
    ease: 'Linear.easeNone',
  }, {
    x: deviceWidth,
    ease: 'Linear.easeNone',
  }
);

 

Then to get it to loop with a delay I'd change the onComplete to something like:

 

this.tl = new TimelineLite({
  onComplete: function() {
    this.delay(2).restart(true);
  },
});

 

Which will produce a result like this one!

 

See the Pen pmWaQL?editors=0010 by ZachSaucier (@ZachSaucier) on CodePen

 

FYI, TimelineMax actually has an infinite repeat built in, so what you're trying to do would be even more simple:

 

this.tl = new TimelineMax({
  repeat: -1, 
  repeatDelay: 2
});

 

  • Like 2
Link to comment
Share on other sites

If you really wanted to continue to do it using two separate .to calls, you would have to change them to keep the same rate of travel by calculating the animation duration yourself (as far as I know). I'm assuming you want the speed of the second .to below:

 

You know the following information: the distance of the first (400) which I will call d1, the distance of the second (deviceWidth) which I will call d2, and the speed of the second (2 seconds) which I will call t2. Thus you can set up an equation like so: rate = d1 / t1 = d2 / t2. Solving for t1, we get t1 = d1 * (t2 / d2). Filling in what we know, we get the following:

 

this.tl
.from(this.moped, 400 * (2 / deviceWidth), {
  x: '-400',
  ease: 'Linear.easeNone',
})
.to(this.moped, 2, {
  x: deviceWidth,
  ease: 'Linear.easeNone',
});

 

Which gives us 

See the Pen gJGvEL?editors=0010 by ZachSaucier (@ZachSaucier) on CodePen

 :smile:

  • Like 4
Link to comment
Share on other sites

18 minutes ago, ericnguyen23 said:

@ZachSaucier Many thanks for fromTo method and TweenMax's repeat and repeatDelay properties!! 

 

Happy to help! 

 

18 minutes ago, ericnguyen23 said:

A side question: what do you recommend for making this animation more believable/organic? ie bumps on the road. 

 

Interesting question. You could use GSAP's RoughEase to do something like this (I moved your biker up some to better show the effect): 

 

See the Pen OYxvNj?editors=0010 by ZachSaucier (@ZachSaucier) on CodePen

 

If you wanted it to be random every time he went across the screen you could change the ease in the onComplete function. I'd recommend putting the generator into its own function though, like so:

 

function getRoughEase() {
  return new RoughEase({ template: Power0.easeNone, strength: 1, points: 20, taper: "none", randomize: true, clamp: true});
}

var bounceEase = getRoughEase();

 

Then changing the onComplete to 

 

this.tl = new TimelineLite({
  onComplete: function() {
    bounceEase = getRoughEase();
    this.delay(2).restart(true);
  },
});

 

and setting the ease of the bumping animation to that ease variable:

 

.to(this.moped, 2, {
  y: 30, // The max height you want to move
  ease: bounceEase
}, "-=2")

 

which produces 

 

See the Pen XweEKw?editors=0010 by ZachSaucier (@ZachSaucier) on CodePen

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

Anyone happen to know how I can store/update window width variable as user changes browser size? Right now I'm just setting it up like this 

const deviceWidth = window.innerWidth;

 

Variable only updates when browser refreshes.  I'd like it to update AS user resizes. 

Link to comment
Share on other sites

Thanks for the links @mikel

 

I updated my codepen and thinking I'm really close. 

 

I created a state and set it to empty:

 

constructor(props) {
    super(props);

    this.state = { winWidth: '' };

  }

 

I then set state to window.innerWidth value everytime window resizes:

 

componentDidMount() {
    window.onresize = () => {
      this.setState({
        winWidth: window.innerWidth,
      });

      console.log(this.state.winWidth); // just so I can see that state is changing
    };
  
  
}

 

I then assigned GSAP timeline x position to new state thinking this would work but doesn't.

 

      this.tl
      .fromTo(this.moped, 4, {
        x: '-400', 
        ease: 'Linear.easeNone',
      }, {
        x: this.state.winWidth,
        ease: 'Linear.easeNone',
      })

 

the Moped is dissappearing early into animation. any help would be appreciated. 

Link to comment
Share on other sites

49 minutes ago, OSUblake said:

You're animation doesn't know if the width value has changed. You'll need to recreate or invalidate your animation every time the window resizes.

 

 

 

hmm ok this makes sense and getting me one step closer. 

 

So what I did now is wrap the animation into a named function, passing the new width as a parameter. I'm calling the animation function within window.onresize, passing new width state.  It's not working but I like where it's taking me. 

 

Going to call it a night and start fresh tomorrow or the next day.

 

@ZachSaucier  @OSUblake Any tips would be appreciated. 

 

See the Pen vwORxr?editors=0010 by ericnguyen23 (@ericnguyen23) on CodePen

 

 

Link to comment
Share on other sites

11 hours ago, ericnguyen23 said:

So what I did now is wrap the animation into a named function, passing the new width as a parameter. I'm calling the animation function within window.onresize, passing new width state.  It's not working but I like where it's taking me. 

 

state is not synchronous, so you're probably not passing in the current state.

https://reactjs.org/docs/faq-state.html

 

But there's really no reason to use state for this as the animation has nothinig to do with react. 

 

See the Pen 7b477a20f760d5691d4d1e20c905db65 by osublake (@osublake) on CodePen

 

 

  • Like 3
Link to comment
Share on other sites

2 hours ago, ericnguyen23 said:

I noticed you had to add GSAP's progress and clear methods as well. 

 

Yep. You need to clear out the timeline to put it in its original state. If you don't, it will just add more animations to the original timeline. For example, if the width was 800, and then a resize happens and the width is 1000. It will play the 800 width animation, and then it will play the 1000 width animation.

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...