Jump to content
Search Community

Random looping animation buggy on multiple elements

oppodeldoc test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hi there, and first off thanks so much for all the support and work on GSAP, it is amazing!

 

We're working on a project where we'd like to have several (like 5-6) dots looking like they are floating a bit. GSAP makes this very easy and we created a simple looping animation that works well on a single element floating around like this:

const float = {
  x: 'random(-20, 20, 5)',
  y: 'random(-20, 10, 3)',
  duration: 'random(1, 4, 2)',
  ease: 'sine.inOut',
  repeat: -1,
  repeatRefresh: true,
}

const floatingThing = gsap.to('.floatMe', float)

The motion is just what we want... randomized within a range and nicely spaced out. Looks great! 

 

The problem is, when we try to simply apply this effect to multiple elements (like more than 2 or 3), it starts to wig out on us intermittently. Sometimes everything floats, sometimes a couple dots don't tween at all, and other times we get a dot that jumps instead of tweens. I attached a minimal demo, you may need to reload it a couple times but you should see it. I've also attached a short screen capture of the problem (check the dot all the way to the left and you'll see it jump instead of tween).  Is there something I'm missing here about repeats or random animations? I've tried lots of variations, including applying separate tweens to each element, using a setTimeout to start the animations at different times... all to no avail. Is this a bug with GSAP or is my approach wrong? 

 

Any help appreciated! I could try and hand roll this but we're using GSAP heavily in this project and it's tantalizingly close!  Thanks again.  -Matt

 

Screencap:

 

 

See the Pen zYaNMxN by oppodeldoc (@oppodeldoc) on CodePen

Link to comment
Share on other sites

Hi @oppodeldoc and welcome to the GreenSock forums!

 

5 hours ago, oppodeldoc said:

first off thanks so much for all the support and work on GSAP, it is amazing!

We definitely love reading comments like this, thanks! 🥳

 

For some reason the duration is not being randomized on every repeat and on top of that with the random function you're passing sometimes you get zero, hence the jumping element. Honestly I couldn't tell you why this is happening, so I'll ping @GreenSock so he can take a look at it and clarify this for us.

 

I did a try with this for the duration and never got zero as a value, so maybe there is an issue with the snapping value:

duration: 'random(1, 4, 1)' // setting snapping to one

When using two as the snap value you get zero for the duration some times. You can inspect that using this code to get the logs on each repeat:

const randomX = gsap.utils.random(-20, 20, 5, true);
const randomY = gsap.utils.random(-20, 10, 3, true);
// Here I'm using the original value for random time you provided
const randomTime = gsap.utils.random(1, 4, 2, true);
// With the following line there is no zero duration
// const randomTime = gsap.utils.random(1, 4, 1, true);

const float = {
  x: (i) => {
    const x = randomX();
    console.log(i, "x value", x);
    return x;
  }, //'random(-20, 20, 5)',
  y: (i) => {
    const y = randomY();
    console.log(i, "y value", y);
    return y;
  }, //'random(-20, 10, 3)',
  duration: (i) => {
    const time = randomTime();
    console.log(i, "time", time);
    return time;
  }, //'random(1, 4, 2)',
  ease: 'sine.inOut',
  repeat: -1,
  repeatRefresh: true,
}

Please stand by for an official explanation on the subject.

 

Happy Tweening!

Link to comment
Share on other sites

  • Solution

Yeah, that's a very strange random value where you're telling it to be between 1 and 4, but round to the closest 2?? Why would you make the floor value 1 (which it could never be if you're snapping to the closest increment of 2)? If your goal is to select 2 or 4, you should do either "random(2, 4, 2)" or "random([2, 4])".

 

Keep in mind that duration and delay are NOT eligible for repeatRefresh: true, as indicated in the docs. It's logically impossible because...well, it'd take a long time to explain it all and show you the various factors where it'd cause the logic to fail; if you want the explanation just let me know but I'll spare you here.

 

You can easily get that effect, though, in a different way. I'll show you in the demo below. 

 

Another problem is that you're using a single tween for the movement of a bunch of elements, so that one tween must have a single progress, duration, etc. so what you're doing is essentially creating timeline internally with a tween for each element and they can all have their own durations but the repeat only occurs when that parent tween finishes. That's not a bug or anything - that's exactly how it's supposed to work but I wanted to point it out because you seem to be wanting everything to be very randomized and organic (It isn't very organic if ALL of the elements are on the exact same refresh cycle). 

 

Here's a better approach that uses a function that gets called in the onComplete to make it infinitely loop but choose new values each time through: 

See the Pen zYaZxGj?editors=0010 by GreenSock (@GreenSock) on CodePen

 

I typically like to add a random delay in there too in order to ensure the refresh cycles look even more randomized/organic. Right now they're all still shifting in increments of every 2 seconds.

 

Does that clear things up? 

  • Like 2
Link to comment
Share on other sites

Excellent, thank you for the reply and explanation.  Your modified code works great for what we're doing. 

 

The snap value is weird because I was playing around with different values to try to get it to work in my minimal demo and I think I focused on that one because it reproduced what I was thinking was an intermittent problem so... negative feedback loop confirmed! :) 

 

By way of understanding this though... are you saying that the jumpiness caused by the zero duration and the fact that some of them never started at all is expected behavior? I tried a lot of different stuff here, including attaching it to each element individually (as in the commented out code I used) and settTmeouts and stuff like that. I guess this all boils down to just needing to set the snap number to a sensible value or not at all? Thanks again for all the help, I'm just curious how the random function snap value relates to all of this and if it returning zero even though the range was set between 1 and 4 is expected. Thanks again!  -Matt

Link to comment
Share on other sites

Yep, the "jumps" were caused by zero-duration sub-tweens. So they'd immediately set their values, wait until the parent tween completely finished (typically 4 seconds because at least one of the children probably had a 4-second randomized duration), and then BOOM, immediately set the next one (because it's zero duration). 

 

It's not really a bug in the source. I mean yes, we could add extra conditional logic just for this one scenario to get around invalid random values like that, but then every random() call would pay that negligible performance and kb price. Seems kinda silly to add that rather than just encouraging folks to use valid values :)

 

Enjoy!

  • Like 1
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...