Jump to content
Search Community

Cannot .set items of class with random start values

spacewindow test
Moderator Tag

Go to solution Solved by Tahir Ahmed,

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

Hi guys,
 
Having issues getting my 'dustcloud' generator function in Greensock.
 
Objective: 
  • Target a created element (in this case, and <image> inside my SVG) and duplicate it to the number of 'clouds' I wanted = SUCCESS
  • TweenMax.set random start value for x: y: and opacity: for 'clouds', without having to target individual class/id names = FAIL. All got set to same, so...
  • Made a new attempt to TweenMax.set random values for attributes by giving individual class names for elements on duplication = SUCCESS. However, when I follow this up with animation using TweenMax.to...
  • Create random destination for clouds using  TweenMax.to values for x: y: and opacity: for individual elements = FAIL. Animation only works for one element, then when animation finishes it SETS the remaining elements.

I'd like to know:

  1. whether the setting can happen on all elements prior to animation.
  2. if this can be done without making individual classes for each element (though this is not essential, just thought it might be more performative or something...)

 

Apologies, I'm sure i've gone about this in a fairly hacky/clunky way, but have been at it for hours – any direction will be appreciated.

See the Pen qbEQJX by spacewindow (@spacewindow) on CodePen

Link to comment
Share on other sites

  • Solution

Hi @spacewindow,

 

Take a look at this forked codepen: 

See the Pen dGPErM by tah_med (@tah_med) on CodePen

and then the JS below:

var tl = new TimelineMax({ repeat: -1 });

// RANDOM NUMBER MIN MAX - https://gist.github.com/timohausmann/4997906
var randMinMax = function(t, n, a) {
  var r = t + Math.random() * (n - t)
  return a && (r = Math.round(r)), r
}

// Returns one single Cloud by duplicating the existing Cloud element
var createCloud = function(element) {
  var newElement = element.cloneNode(true);
  element.parentNode.insertBefore(newElement, element.nextSibling);
  return newElement;
};

// Initial placement of all clouds objects
var placeClouds = function(clouds) {
  TweenMax.staggerTo(clouds, 0, {
    cycle: {
      x: function() { return randMinMax(-200, 200); },
      y: function() { return randMinMax(0, 100); },
      scale: function() { return randMinMax(0.4, 1); }
    },
    opacity: 0
  }, 0);
};

// Addition of all cloud elements into main TimelineMax instance
var addIntoMainTimeline = function(clouds) {
  tl.staggerTo(clouds, 4, {
    cycle: {
      x: function() { return randMinMax(-200, 200); },
      y: function() { return randMinMax(-150, -200); },
      scale: function() { return randMinMax(0.4, 2); }
    },
    bezier: [{ opacity: 0.8 }, { opacity: 0 }],
    ease: Power1.easeInOut
  }, 0);
};

var clouds = function(cloud, numClouds) {
  var clouds;
  var element = document.querySelector(cloud);
  for (var i = 0; i < numClouds; i++) { createCloud(element); }
  clouds = document.querySelectorAll(cloud);
  placeClouds(clouds);
  addIntoMainTimeline(clouds);
};

clouds('.dust', 4);

Main takeaways are:

  1. The use of .staggerTo methods. Take a look at the documentation for more details on it: TweenMax.staggerTo() static method and TimelineMax's .staggerTo() instance method. Especially the use of `cycle` property introduced in these methods in version 1.18.0.
  2. The usage of BezierPlugin. See documentation for more details.

Let us know if you have any questions after that.

  • Like 2
Link to comment
Share on other sites

Thanks Tahir, this is awesome. That stagger method is interesting.

 

After examining your code, I also reworked my version using Javascript 'for' loops: http://codepen.io/spacewindow/pen/qbEQJX

 

Though I don't think performs amazingly, as it currently does everything 'inline' as it were (my for loop is not cached in a function). I will fix this.

 

Do you have an opinion on what is more performative – using the stagger/cycle method, or basic for loops?

 

My other learning in all this has been to use a loop function to generate the timelines for each cloud individually, then add them to a main timeline.

 

I think I was overwriting parts of my single timeline as I went by trying to add tweens there immediately – hence the weird effect where the clouds were being .set() at the completion of animation, rather than at the start. Though this behaviour is still confusing to me. I don't know why the whole animation didn't just fail instead of partially working :)

 

Thank you so much!

Link to comment
Share on other sites

Hi @spacewindow

First off, `.stagger...()` methods, if I understand them correctly, are convenient methods. You can totally do without them. I am not sure if they add any performance value, I guess this is something @GreenSock can comment on.

Having said that though, I would definitely recommend looking into `.stagger...()` methods as they don't only abstract the loops away (hence less code to write) but it also comes equipped with a few more convenient tricks.

 

For example, consider my pen again, and assume that you wanted to generate 10 clouds in total.

 

Now imagine if you wanted to place each of the `cloud` duplicate that gets created in 3 absolute positions (e.g. `x` to be placed on 100, 150 and 200 pixels but alternating between them in a way that the first `cloud` instance gets to be placed at 100, 2nd on 150, 3rd on 200, 4th on 100 again and so on ...).

 

Now of course this can be written purely in vanilla JS but `.stagger...()` syntax of the same would look like: 

TweenMax.staggerTo(clouds, 0, {
  cycle: {x: [100, 150, 200]}
});

Easy-peasy? All I can say is that I am a big fan of stagger methods :)

 

And yes, originally, you were re-populating the same timeline over and over again which is what was the main reason of the weird end-results you were getting.

Link to comment
Share on other sites

Yes I also realise my pen is still setting individual classes on the elements in order to target my animations – which yours did not require in the createClouds function. Setting the individual classes not necessarily a bad thing? But good to cut out unnecessary steps.

 

You've convinced me, I'm going to use a stagger method for this - being able to set an even spread of dustclouds to begin with is going to be useful.

 

Thanks a lot for helping out so quickly!

Link to comment
Share on other sites

Yeah. Setting individual classes to elements is a bit redundant in this case. Even if you continue to use your approach, i.e. looping, you still shouldn't really need to give them unique class names. The duplication should carry over the default `cloud` class name that you gave to your original element. And then, you can use that to `querySelectorAll` to return you the array of items containing `cloud` as their class names.

 

But yeah, `stagger` methods are pretty darn cool. And with the recent update of being able to `cycle` through the items, took it to another level entirely.

 

Glad I could help.

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