Jump to content
Search Community

How to work with new labels object in GSAP 3

Carl test
Moderator Tag

Recommended Posts

With prior versions of GSAP, TimelineMax had a getLabelsArray() method which returned and array making it very easy to dynamically loop through the array of label objects and do stuff like create nav buttons for each label.

 GSAP code below shows how I would commonly work with the labels array:

 

let tl = new TimelineMax()
.add("milk", 1)
.add("cookies", 2)
.add("beans", 3)
.add("rice", 4)

let labels = tl.getLabelsArray();

console.log("first label is " + labels[0].name)
console.log("second label is " + labels[1].name)
console.log("last label is " + labels[labels.length-1].name)
console.log("number of labels is " + labels.length)

codepen for v2

 

In GSAP 3 we get a labels object, which admittedly I'm not as familiar working with.

Below is a pen setup with GSAP3 if someone could fill in the console logs that would be very helpful

 

See the Pen rNVaVYy by snorkltv (@snorkltv) on CodePen

 

 

See the Pen GRJgJBL?editors=0011 by snorkltv (@snorkltv) on CodePen

Link to comment
Share on other sites

Hey Carl. Depending on your needs you can sort the object in different ways:

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

 

This might be a place where a helper method of some sort would be useful because the object is admittedly harder to work with. What do you see the common use cases being? What sort of method would be the most helpful?

  • Like 1
Link to comment
Share on other sites

Thanks, for the speedy reply. That's definitely the type of stuff I found on stackoverflow that scared me into posting here. Having an array in v2 served my needs perfectly. I guess I'm struggling to see the advantage of the new object approach now that I'm trying to do something with it. I sense Jack chiming in soon ;)

 

Link to comment
Share on other sites

NP. thanks for the update.

 

BTW below is an example of what I'm working on for a lesson. It's done in V2. 

The idea is to show how easy it can be to do basic timeline navigation using tweenTo() with nextLabel() and previousLabel(). Notice how when you run out of labels going forward it takes you back to the first label and it loops the other way clicking on previous. Fairly trivial when working with an array of labels.

 

See the Pen jOPEbwL?editors=0011 by snorkltv (@snorkltv) on CodePen

Link to comment
Share on other sites

Here's why I shifted to a simple labels object in v3: 

  • Reduce kb. I highly doubt even 0.1% of users would need getLabelsArray() and it's super easy to do with an external helper function (below)
  • Reduce API surface area
  • It's faster and simpler for people to figure out the time associated with a label - instead of calling some helper function like getLableTime(), they can simply do timeline.labels.whatever. Done. 
  • Everything was stored in a labels object anyway in v2, so the plumbing is identical. I just changed it from "_labels" (underscore indicating private) to "labels".

That being said, here's a simple helper function that'll give you the same results as in v2: 

function getLabelsArray(timeline) {
  let labels = timeline.labels,
      a = [],
      cnt = 0,
      p;
  for (p in labels) {
    a[cnt++] = {time: labels[p], name: p};
  }
  a.sort((a,b) => a.time - b.time);
  return a;
}

Does that help? 

  • Like 3
Link to comment
Share on other sites

Thanks for the explanation and helper function. I still think removing the ability to access a label via its index in an array is something that should be baked-in.

 

It's nice that the reasons for its removal and helper function are documented here.

  • Like 1
Link to comment
Share on other sites

Here's an even shorter, more modern version of that function:

const getLabelsArray = timeline => Object.keys(timeline.labels).map(v => ({name: v, time: timeline.labels[v]})).sort((a,b) => a.time - b.time);

 

22 minutes ago, Carl said:

I still think removing the ability to access a label via its index in an array is something that should be baked-in.

Can you elaborate a bit on that? Why do you think it's worth the kb and expanded API surface area? Do you think this is a common thing that a lot of people need and they wouldn't be able to find/make/use the helper function? I don't recall many questions in these forums that show it's a high-demand kinda thing, but maybe that's just because people don't know they can ask here. Are you seeing demand for it in other circles? 

  • Like 1
Link to comment
Share on other sites

2 hours ago, GreenSock said:

Here's an even shorter, more modern version of that function:


const getLabelsArray = timeline => Object.keys(timeline.labels).map(v => ({name: v, time: timeline.labels[v]})).sort((a,b) => a.time - b.time);

 

 

@Carl I know you are trying to focus on the animation part in your lessons, but I think that function is a JS fundamental. Well,

everything except the sort part as that is more advanced. But definitely the keys and map part.

let myArray = Object.keys(obj).map(key => obj[key]);

 

That's the same as doing Object.values.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values

 

And according to your demo, you really don't even need the label names. You just need the times, right? So this might be much simpler for your use case.

let labelsArray = Object.values(tl.labels).sort();
let index = 0;

tl.tweenTo(labelsArray[index++]);

 

 

 

  • Like 3
Link to comment
Share on other sites

 

23 hours ago, GreenSock said:

Can you elaborate a bit on that? Why do you think it's worth the kb and expanded API surface area? Do you think this is a common thing that a lot of people need and they wouldn't be able to find/make/use the helper function? I don't recall many questions in these forums that show it's a high-demand kinda thing

Any time you remove something people are used to you're going to get some pushback. Look at className tweens. We didn't think those made much sense but it now seems people came to rely on them and structure their projects around them. Filesize and API surface area really aren't big concerns for me. I'd gladly sacrifice those for the convenience. I'm guessing you didn't see many questions about getLabelsArray() because it was intuitive and easy to use. It's one of those things that when people realize they need something like it they go to the docs and voila it's right there and it works as they expect. Time will tell if more people actually miss it. The helper function is a good enough solution for now. thx.

 

 

Hey @OSUblake Thanks for chiming in. I ALWAYS appreciate your perspective and advice.

In addition to focusing on animation I try really hard to keep the entry level low and make everything I teach look really easy or at least not scary. I'm always thinking of how I would teach these things to 7th graders. I love the moments of being able to say: "Oh you want to do x? GSAP has a method for that. Look what this one line of code does..."

 

On the other-hand I'm not going to deny that map() and Object.values() aren't helpful. I've been thinking of putting a chapter together that contains some of these "things you should know" things. BTW I didn't know about Object.values() that's pretty cool. And yes, I can use the time or labelName in my example. Ultimately I still feel that looping through an array is the easier concept to grasp, maybe i'm just stuck in my old ways.

 

Again, thanks for the suggestions! I'm always learning new things from you and this group.

 

 

 

 

  • Like 2
Link to comment
Share on other sites

On 2/8/2020 at 1:10 PM, Carl said:

In addition to focusing on animation I try really hard to keep the entry level low and make everything I teach look really easy or at least not scary. I'm always thinking of how I would teach these things to 7th graders. I love the moments of being able to say: "Oh you want to do x? GSAP has a method for that. Look what this one line of code does..."

 

Yeah, I know you are trying to make it approachable, and that's hard because people want to jump right into the cool stuff. All I can say is that the majority of questions on this forum usually aren't GSAP questions at all. They mostly seem to be general JavaScript questions. 

 

On 2/8/2020 at 1:10 PM, Carl said:

BTW I didn't know about Object.values() that's pretty cool.

 

It's only been around for a couple of years, so a lot of people don't know about it.

 

On 2/8/2020 at 1:10 PM, Carl said:

Ultimately I still feel that looping through an array is the easier concept to grasp

 

In your demo it does, but having a labels object also has it pros. For example, it is much easier to get and set values in an object because you don't need to use an array index to find the label.

 

//get
var time = tl.labels.myLabel

// set
tl.labels.myLabel = 4;

 

 

  • Like 4
Link to comment
Share on other sites

@ilovemypixels please do let us know if anything else isn't working as you'd expect. As for getLabelsArray(), were you saying it's particularly painful for you to use the helper function instead (thus prefer it gets added back into the API) or just that you didn't realize the adjustment was necessary? 

 

If we get a lot of feedback about the need for that function, I'm certainly willing to reconsider but it just seems kinda unnecessary. Sometimes my judgment doesn't align with most of the community, though, and there's some compelling reason that I just missed. 

Link to comment
Share on other sites

Then maybe it should just be added to the list of helper functions. The code in v2 seems pretty straightforward.

https://github.com/greensock/GSAP/blob/86b54d1c70237daeb754f6be8b5d9ee4f4c9cbe2/src/esm/TimelineMax.js#L400

 

p.getLabelAfter = function(time) {
  if (!time)
    if (time !== 0) {
      //faster than isNan()
      time = this._time;
    }
  var labels = this.getLabelsArray(),
    l = labels.length,
    i;
  for (i = 0; i < l; i++) {
    if (labels[i].time > time) {
      return labels[i].name;
    }
  }
  return null;
};

p.getLabelBefore = function(time) {
  if (time == null) {
    time = this._time;
  }
  var labels = this.getLabelsArray(),
    i = labels.length;
  while (--i > -1) {
    if (labels[i].time < time) {
      return labels[i].name;
    }
  }
  return null;
};

p.getLabelsArray = function() {
  var a = [],
    cnt = 0,
    p;
  for (p in this._labels) {
    a[cnt++] = { time: this._labels[p], name: p };
  }
  a.sort(function(a, b) {
    return a.time - b.time;
  });
  return a;
};

 

  • Like 1
Link to comment
Share on other sites

3 minutes ago, ilovemypixels said:

I was assuming that as well as getLabelsArray() things like previousLabel(), nextLabel() would have been removed. Looks like that is not the case.

 

Ha. I didn't realize that either. So the only thing missing is the array part, which can be done using one of the functions above.

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