Jump to content
GreenSock

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

animation in a loop

Go to solution Solved by OSUblake,

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

Hello there

 

I wrote a little function that is supposed to animate the transition of text blocks from one value to another.

in a nut shell, it is supposed to change the innerHTML of a an element from one letter to another. However if the current value is 'a' and the desired value is 'e', than the function will cycle through 'b','c','d' to finally stop at 'e'. the logic seems to work fine and it indeed cycles through the letters correctly to get the proper end message to display.

 

where i am having trouble is animating the transition. Since the actuall animation function is inside a loop, it only animate the last transition. you can see what happens in the  attached code pen if you remove the // from line 116 and 117 .

 

how would i animate every stage of the transition?

 

thank you!

See the Pen mejOpz?editors=001 by ohavben (@ohavben) on CodePen

Link to comment
Share on other sites

This would be perfect for the ScrambleText Plugin. However, there's another plugin that might help you out. It's not documented so nobody really knows about it, but its deals specifically with arrays, the EndArray Plugin. I just animated an array of letters from 'hello' to 'world'. 

 

See the Pen ba63e145ad0f467cce80783c7ed92cac by osublake (@osublake) on CodePen

  • Like 1
Link to comment
Share on other sites

whoa !! greensock has a pulg in for that?

 

that's really cool and i will definitly check it out.

 

however i would still like to solve this problem sans plugin, mainly because I am new to javascript and programming in general and i feel like i am missing something. I read about closure and loops and i understand that i should wrap the animation inside an anonymus function inside the loop, but i am not sure how to go about it. 

 

Thank you for the codepen example!

Link to comment
Share on other sites

Just curious. Where did you read about using closures and wrapping the animation in an anonymous function?

 

Creating animations inside a loop can be tricky because it's going to cycle through the entire loop before your animation even starts. I didn't look closely at your code, but if you are animating the same object in your loop, you should probably use a timeline to make sure that each animation will play. Otherwise, it will just play the last one created in the loop if you're using the same properties in all your animations.

Link to comment
Share on other sites

i can't say where I read about it exactly, just been googling.

 

but i tried the following code which produced the same results: 

for ( var d = 0 ; d < textDifference; d++){
           (function(newd){
                                        
                     switch(textDirection) {
                              case '--':
                                       diffValue--;
                                       if (diffValue < 0 ) {diffValue = 41} // lettarArray length is 41
                                  break;

                              case '++':  
                                       diffValue++;
                                       if (diffValue > 41  ) {diffValue = 0}
                                  break;
                              }
                        
             myCurrentDiv = document.getElementById('textdiv'+v); 
            // toggle the next line to see the end result of the effect
            setTimeout(function(){ myCurrentDiv.innerHTML = letterArray[diffValue]; }, 250);
            // myCurrentDiv.innerHTML = letterArray[diffValue];
            })(d);  
}  

are you saying that I could use a timeline to simulate a loop?

 

i have two loops, the outer one iterated through objects and the inner one iterates through values for the current object,

 

so even if I replace the inner loop with a time line, i may still be facing the same problem.

Link to comment
Share on other sites

I think what you were reading about closures and anonymous functions has to deal with this problem. I created 5 buttons, and they are labeled correctly, but when I click on them the wrong number is logged to the console. But that's a lesson for another day.

 

See the Pen XmBPdO?editors=001 by osublake (@osublake) on CodePen

 

With the code you just posted you are still creating the same problem again. Don't worry though, it's pretty easy to overlook if you're new to programming. This is actually similar to a problem I had years ago trying to stagger cards flipping using a loop. You're setting a timeout for 250 in a loop. Every iteration is going to start the timeout at the same time. See if you can figure out why #box1 is not moving in this animation but #box2 is. What you are doing is exactly what is happening to #box1.

var pos = [100, 50, 150, 0];

var tl = new TimelineLite();

for (var i = 0; i < pos.length; i++) {  
  TweenLite.to("#box1", 0.5, { x: pos[i] });
  tl.to("#box2", 0.5, { x: pos[i] });
} 

See the Pen b5a61eeb6bb9c489dc05e03dacd4daba?editors=110 by osublake (@osublake) on CodePen

  • Like 2
Link to comment
Share on other sites

well it does animate #box1 but only to the last value because it executes after the last iteration which is 0 , so essentially animating from 0 to 0.

 

I'm not excatly sure what is the difference between tl.to and tweenLite.to in this context that will allow #box2 to animate for every iteration. I'm guessing that scope for tl. is somehow different because it's meant to hold many functions?  

 

I noticed that I changed your code like so

var pos = [100, 50, 150, 0];



for (var i = 0; i < pos.length; i++) {

  var tl = new TimelineLite();
  TweenLite.to("#box1", 0.5, { x: pos[i] });
  tl.to("#box2", 0.5, { x: pos[i] });
} 

it changed the animation of #box2 entirely

Link to comment
Share on other sites

Ah good! You caught that it still tweened even though it was to 0. When you move the timeline inside the loop like that, it's creating a new timeline on every iteration, so it's basically doing what the TweenLite.to is doing.

 

So the reason the timeline animation works is because they are being added to the timeline and are set to start at different times, one right after another. By just using TweenLite.to in the loop, all the animations will start at the same time, so only the last one will play. A loop does not wait on the animation to finish before moving on the next iteration. So to fix the TweenLite.to box animation is pretty easy. You just need to add a delay to each animation, and it will run in sync with the timeline.

var pos = [100, 50, 150, 0];

var tl = new TimelineLite();

for (var i = 0; i < pos.length; i++) {
  
  TweenLite.to("#box1", 0.5, { x: pos[i], delay: 0.5 * i });
  tl.to("#box2", 0.5, { x: pos[i] });
} 

See the Pen f3767edb0aaac0f6294c001449876962?editors=001 by osublake (@osublake) on CodePen

 

Fixing the start time of your callbacks/animation is one part of your problem. I'm not sure how you plan on dealing the time difference between letter changes. Going from a-z will require more steps than going from y-z, thus it will require more time.

 

For the closure issue, I think it's much easier to just create a function that is outside of your loops, and just pass everything that is needed to it.

// Outer loop
for (var i= 0; i < foo.length; i++) {
  
  // Do some stuff...

  // Inner loop
  for (var j = 0; i < bar.length; j++) {
   
    // Do more stuff...
    
    // Call the function
    setLetter(some, vars, blah)
  }
}

function setLetter(some, vars, blah) {

  // Create the letter animation in here
  // This will create the closure with the proper scope
}

Does that help out?

  • Like 1
Link to comment
Share on other sites

Also, you can avoid a lot of closure issues by using forEach on an array instead of a for loop since it's already inside a function.

var myLetters = ["h","e","l","l", "o"];

myLetters.forEach(function(letter, i) {
  // Do your loop stuff in here
  console.log("Letter " + letter + " is at index " + i);
});

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Link to comment
Share on other sites

 

 

Fixing the start time of your callbacks/animation is one part of your problem. I'm not sure how you plan on dealing the time difference between letter changes. Going from a-z will require more steps than going from y-z, thus it will require more time.

 

 

 

well I haven't considered the time difference, maybe because i haven't seen the result yet. but what I am shooting for is being able to go from a-z sequentially. for example if the 'to' message is "z" and the 'from' message is "a" it will cycle up , but cycle down for the opposite. 

 

I tried both declaring a time line outside the function and the following code: 

See the Pen epjPKd?editors=001 by ohavben (@ohavben) on CodePen

TweenMax.to(nullObject, 0, {
                 delay: 0.25 * d,
                 onComplete: function(){myCurrentDiv.innerHTML = letterArray[diffValue]},
                 });
  
tl.to(nullObject, 0.25, { onComplete: function(){myCurrentDiv.innerHTML = letterArray[diffValue]}})

but it didn't change anything. 

 

I like your last suggestion and I will give it a go. it seems like a good thing to know ;)

Link to comment
Share on other sites

  • Solution

The problem with that one is that diffValue changes on every iteration, so it's doing the same thing as that button demo. I added a function outside the loop to handle this. I also used TweenLite.delayedCall since you're really not doing an animation. 

 

See the Pen LpBXYr?editors=001 by osublake (@osublake) on CodePen

Link to comment
Share on other sites

Thank you so much for the solution :) 

 

so i see how it works now , you declare your function in advance and do the loops to get the arguments. when the function is called it lives and dies for each iteration. so even when it works correctly it creates a number of function that start at the same time?

 

it's not the effect i imagined because i wanted to change each letter individually , but  i'm happy to see how it looks 

Link to comment
Share on other sites

Well you can rework it to do each letter individually, but it sounds like you are understanding what is going on now. Each time that function is called it's creating a new scope that is different than the one in the loop, so what happens in the loop will no longer effect stuff that is happening in the function. I know, it's confusing. JavaScript is just a weird language, and it can be really hard to explain how things work unless you do it yourself through trial and a lot of errors.

 

Here's a quick lesson in closures. See if you can figure out what's going on.

 

See the Pen ojqrXv by garethredfern (@garethredfern) on CodePen

  • Like 1
Link to comment
Share on other sites

thank you it did click for me today !

 

i think i will use stagger() to replace the outer loop that should make things a lot easier and will have much better control

  • 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.
×