Jump to content
Search Community

Add tween/label after the last one added, not at the end of the timeline

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

Normally, adding a tween without a position parameter will add it after ALL the tweens. Is there an easy way to add a tween right after the last tween? This without manually using variables, manually sorting the .to calls and without creating sub-timelines (ideally)

 

 

In the example, tl.addLabel("after-bluebox"); adds the label BEFORE the tween, if not present. I think that there could be a way to add it AFTER the tween, perhaps tl.addLabel("after-bluebox", "", true); where true means "after the last tween you added"

See the Pen uHrdk by bfred-it (@bfred-it) on CodePen

  • Like 1
Link to comment
Share on other sites

Hi,

 

Thanks for the suggestion and for providing the demo (very helpful). I think it would make more sense just to give the position parameter another possible value like "lastTween" instead of adding an additional parameter. Also consider the fact that many methods use the position parameter (to(), from(), fromTo(), staggerFrom(), staggerFromTo(), call(), addPause() etc) so it wouldn't be a small addition to the API.

 

Currently, there is no super easy way of doing what you need, but it surely is possible using nested timelines or variables tracking durations and offsets.

I imagine you have a much more complex implementation in mind, but your CodePen example could handle it just by adding the first tween last:

 

tl.to("#blueBox", 1, {x:550}, 1);
tl.addLabel('after-bluebox');
tl.to("#blueBox", 1, {rotation:360}, 'after-bluebox');
tl.to("#redBox", 5, {x:550}, 0); //lastly, put the long tween at a time of 0

Again, your suggestion has merit, we would just have to consider how it would impact the API in terms of filesize and how it would impact our users. 

Link to comment
Share on other sites

One other idea: what if TimelineLite and TimelineMax had a chainMode() method that allowed you to control the default insertion point. By default, it'd be at the end of the timeline itself (current behavior), but if you called yourTimeline.chainMode(true), it'd always use the ending time of the most recently added tween/timeline as the insertion point. Like:

tl.chainMode(true)
  .to("#redBox", 5, {x:500})
  .to("#blueBox", 1, {x:500}, 1)
  .to("#blueBox", 1, {rotation:360}); //will be added immediately after the blueBox finishes moving horizontally)

And of course you could use tl.chainMode() to get the current value too, for inspection. 

 

What do you think? Got any better ideas? My concerns about having a special string like "lastTween" are:

  1. I'm having a tough time figuring out a name that'd fit. "lastTween" doesn't sound right if the last thing added was a timeline or callback. It also might be ambiguous in terms of "is it at the start of that tween or at the end?" It'd be silly to use "afterMostRecentlyAddedChild" (too long). Maybe we could use "lastChild" or something.
  2. It could get messy if someone accidentally uses that reserved keyword as a label itself.
  3. It requires that the user add this keyword for the position parameter every time. If they want to chain things, that might get old, like tl.to(..., "lastChild").to(...., "lastChild").to(..., "lastChild")... whereas if we use chainMode(), it's one flag they set and thereafter things just work the way they like. 

Also, does anyone have a better idea for a name than "chainMode()"? It could be "chaining()". 

Link to comment
Share on other sites

appendMode() ? The append method itself was deprecated, but that's the word we usually use to describe the equivalent behaviour here.

 

Perhaps braces could be used to indicate the reserved word, similar to {self} in callback params. Something like {appendToRecent} ?

tl.to("#redBox", 5, {x:550})
  .to("#blueBox", 1, {x:550}, 1)
  .to("#blueBox", 1, {rotation:360}, "{appendToRecent}");

As a third option, how about a new public method that would return append positions for the most recent child? e.g.

TimelineLite.appendPosition(returnStartTime:Boolean = false):Number

// usage
tl.appendPosition();     // end time for most recently added child
tl.appendPosition(true); // start time for most recently added child
tl.to("#redBox", 5, {x:550})
  .to("#blueBox", 1, {x:550}, 1)
  .to("#blueBox", 1, {rotation:360}, tl.appendPosition());
 

The chainMode approach seems it would be the handiest when inserting a long (or even repeat:-1) tween early in a timeline.

Link to comment
Share on other sites

I have to say that I like Jamie's suggestion to use a method to retrieve the appendPosition() as it seems fairly easy to add to the API and use.

 

However since both of you noted that anything you would have to use repeatedly like "lastChild" position flag or this new appendPosition() method would be cumbersome in long timelines, I would go with something along the lines of chainingMode().

 

My counter argument though is that the position parameter is quite flexible as is, and as such quite a mouthful to explain properly: http://greensock.com/position-parameter

 

Let's take a look at the current explanation from the docs and how it may need to be altered (in red)

 

The 4th parameter is the position which controls the placement of the tween in the timeline (by default, it's at the end of the timeline, unless chainMode() is true, in which case it will be the end of the previous tween, nested timeline or callback). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline, unless chainMode() is true, in which case it will be the end of the previous tween, nested timeline or callback. For example,"+=2" would place the tween 2 seconds after the end(unless chainMode() is true, in which case it will be the end of the previous tween, nested timeline or callback), leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline (unless chainMode() is true, in which case it will be the end of the previous tween, nested timeline or callback) before inserting the tween which can be quite convenient.

 

I'll grant that might be a little overboard, but I want to illustrate the complexity it adds to many methods. I guess another approach is just to add a paragraph to every position parameter definition explaining that "the end" of a timeline may vary based on the value of chainMode().

 

I have to say that after deeper thought, I'm back at thinking that a simple appendPosition() method might be the way to go. I'm not so sure its worth making the API more confusing for such an edge-case that can be solved in a simpler way.

 

Currently methods using the position parameter work in these 5 ways, with the following 3 bold caveats if chainMode is added.

 

position omitted: object is added at end of timeline OR end of last inserted object

absolute: object is added at absolute time

relative : object is added relative to end of timeline OR end of last inserted object

label: object is added at label if one exists OR a label will be created at end of timeline automatically OR if chainMode(true) a label will be added at end of last inserted object

relative to label: object is added relative to a label.

 

So now 1 parameter has 8 different uses. That's pretty heavy.

  • Like 1
Link to comment
Share on other sites

All very valid points. Are you both saying that you'd ultimately prefer the code to look like this:?

tl.to("#redBox", 5, {x:500})
  .to("#blueBox", 1, {x:500}, 1)
  .to("#blueBox", 1, {rotation:360}, tl.appendPosition())
  .to("#yellowBox", 1, {x:550}, tl.appendPosition())
  .to("#yellowBox", 1, {rotation:360}, tl.appendPosition())
  .to("#greenBox", 1, {x:550}, tl.appendPosition())
  .to("#greenBox", 1, {rotation:360}, tl.appendPosition());

I totally see why appendPosition() would be appealing, especially because it reduces the complexity of explaining the append behavior if we went with chainMode(), but I'm worried that it'll prove cumbersome in practical usage. It locks you into having to type out tl.appendPosition() on every tween thereafter. 

 

Of course the other down side to chainMode() is that the same code could behave two completely different ways based on whether or not you've set chainMode() to true or false. 

 

:|

Link to comment
Share on other sites

Yeah, that's a fair summary of what I'm saying.

And I agree with the point of how the same code will have different meanings and also be confusing... especially in a trouble shooting context. 

 

I just wonder how often this situation comes up or can't easily be solved by adding the long animation last at a time of 0 like

tl.to("#blueBox", 1, {x:500}, 1)
  .to("#blueBox", 1, {rotation:360})
  .to("#yellowBox", 1, {x:550})
  .to("#yellowBox", 1, {rotation:360})
  .to("#greenBox", 1, {x:550})
  .to("#greenBox", 1, {rotation:360});
  .to("#redBox", 5, {x:500}, 0)

If I had to vote, I'd say let's add the easy "end point retrieval method" so folks can get out of a jam quickly when they need to... and then consider the larger implications of adding chainMode() to the API and perhaps judge if its really needed / worth it.

 

 

  • Like 1
Link to comment
Share on other sites

I would go with something similar to what Jamie proposed. Create a chainable method that affects only the current timeline and acts like a setter/getter of the specific behaviour.

var tl = new TimelineLite();

// the default value is false, which means the new instance is added at the end of the timeline
// this acts as a getter
var currentChain = tl.chainMode();//returns false, good for conditional logic

// sets the new behaviour
// also it should return the instance for chainability
// the new instance is added to the end of the timeline's latest instance
tl.chainMode(true).paused(true);

Also it'd be great if the feature could be toggled between true or false, so if the user wants just some instances after the last instance but later on the timeline would like the new instances added at the end of the timeline, something like this:

var tl = new TimelineLite();

tl
  .to("#greenBox", 5, {x:200})
  .to("#greenBox", 1, {y:200}, 1)//starts at the 1 second mark
  //set the chainMode to true in order to add the new instance relative to the previous
  .chainMode(true)
  .to("#blueBox", 1, {rotation:360}, "+=1")//starts 1 second after the previous instance has ended. starts at 2 seconds ends at 3 seconds
  .to("#redBox", 1, {rotation:360})//starts at 3 seconds - ends at 4 seconds
  // set the chainMode to false, all instances will be added to the end of the timeline
  .chainMode(false)
  .to("#greenBox", 1, {rotation:360});//starts at 5 seconds - ends at 6 seconds

Something like that seems a bit more intuitive to me and makes it easier to deal with. Typing a specific object or parameter in every instance added to the timeline seems too cumbersome to me, I find it easier to just turn the feature on/off as needed.

  • Like 1
Link to comment
Share on other sites

It sounds like Carl is in favor of something like tl.appendPosition(), Rodrigo likes tl.chainMode(), and I'm not quite sure where Jamie was leaning. I'm still on the fence.

 

To the point about the same code behaving differently based on a previous setting, we actually already have that with smoothChildTiming anyway. As far as troubleshooting goes, I'm not concerned about the chainMode() thing because it's easy to add one line to the code to ensure that it's whatever we're expecting. Heck, it may even make it easier because if someone writes 50 lines of timeline code, expecting it to work in the "chained" way and they have a long tween at the beginning, it's simple for us to add a single line of code and say "fixed." - no need to add all those tl.appendPosition() values. 

 

After reading through the Web Animations spec and seeing some of the discussions there, I'm keenly aware of the two types of conversations that tend to happen at times like this: academic/theoretical vs. practical. For example, some of the people designing the spec seem to come from a sterile, academic perspective where they think transform matricies should allow any order-of-operation, but then there's another camp who understand that the real people in the trenches don't care about that - instead, they want to be able to control scale, rotation, position, and skew independently and have them work consistently across the board without depending on making animation calls in a specific order. 

 

It's a little bit like a team of engineers designing a car, but none of them have actually driven one. 

 

I don't think anyone here is leaning far into the "theoretical/academic" camp - I'm just encouraging us all to look at this from the most practical standpoint we can. Of course "practical" means different things to different people. Is it "practical" to have a position parameter that takes 3 long paragraphs to describe because it's so insanely flexible? No. Is it "practical" to have people add ", tl.appendPosition()" to all of their chained timeline calls just so we can keep the descriptions shorter/easier? Perhaps not. Which one is more important? Which one impacts the everyday life of a developer most? If you put yourself in their shoes, what would YOU prefer? 

 

Yes, tl.appendPosition() would technically deliver a solution to this problem and it would prevent us from having to add a bunch of qualifiers and descriptions to the position parameter docs (I love that), but I wonder if in real-world use, people would very quickly get sick of typing "tl.appendPosition()" and littering their timeline code with that, wondering "why the heck can't GreenSock just offer a setting that makes this work the way I want it to?"

 

I personally lean toward chainMode() because it's probably the behavior that most people want anyway, so it gives them a very easy "win" to add that one line and keep their code concise. But again, I'm not 100% sold yet. 

 

Jamie? Jonathan? Others? Please chime in - I'd love to hear your perspectives and some healthy debate.

  • Like 1
Link to comment
Share on other sites

In my opinion, I think the example we're working with here is pretty easily solvable without any new APIs. A small amount of short tweens at the start of a timeline seems quite easy to re-order, or use absolute positions on. Modifying the append points to set up a handful of tweens seems to be over-complicated in that case.

 

That said, we can't just assume that all timelines won't be complex monsters in which some new APIs could be handy. Here's a few cases to consider:

 

- append something relative to the most recent added child, and not the timeline's duration

var tl = new TimelineLite();

tl.to(foo, 20, {...}) // long tween
   // assume a relative start is easily calculated for this one
  .to(bar, 2.67, {...}, "-=17")
   // we can't currently append to the previous tween so any relative positions need to be chained together - complexity continues to rise as more tweens are appended here
  .to(baz, 3, {...}, "-=14.33")

// PROPOSED
  .to(baz, 2, {...}, tl.appendPosition());

- still be able to position things relatively after adding an infinite repeating tween

tl.to(foo, 20, {repeat:-1, ...}, "+=2") // infinite tween
   // we want to start 2 seconds after the infinite tween starts so we have to calculate an absolute position
  .to(bar, 2.67, {...}, 4)
   // we can't currently append to the previous tween so absolute positions need to be used for the rest of this timeline - relative values have been made useless
  .to(baz, 3, {...}, 6.67)
 
// PROPOSED
  .to(bar, 2.67, {...}, tl.appendPosition(true) + 2)
  .to(baz, 3, {...}, tl.appendPosition())

- still be able to position many things relatively after adding an infinite repeating tween

tl.to(foo, 20, {repeat:-1, ...}, "+=2")
  .to(bar, 2.67, {...}, 4)
  .to(baz, 3, {...}, 6.67)
  // etc

// PROPOSED
  .to(bar, 2.67, {...}, 4) // the reference still needs to be set after the infinite tween
  .appendToRecent(true) // not totally sold on chainMode to describe this - how about appendToRecent?
  .to(baz, 3, {...})
  // etc

- side effects of changing the append mode

tl.to(foo, 20, {repeat:-1, ...}, "+=2")
  .appendToRecent(true)
  // append point here is still at infinite - no way to reference an early time relatively
  .to(bar, 2.67, {...}, 4)
  // appends to the previous tween as expected
  .to(baz, 500, {...})
  // append point is pushed way ahead again so we have to jump back with absolute or relative values (same as we currently have, to be fair)
  .to(qux, 1, {...}, 0)
  // now we are appending at 1 second again - the append point is much more dynamic in this mode
  .to(quux, 1, {repeat:-1, ...})
  // append point is at infinite again...

// PROPOSED
// no idea. this is bloody confusing...

It seems like appendPosition() would benefit from being able to ignore repeats as well

TimelineLite.appendPosition(returnStartTime:Boolean = false, ignoreRepeats:Boolean = false):Number 
and if it's implemented, an alias could alleviate the issue of typing it many times in longer chains.

 

It does seem like a lot of this can just be solved by the use of labels and ordering of tweens... anyway if I was to choose, I'd be happy with an appendPosition() function. It just seems more intuitive to me; at least at the complexity levels we usually deal with.

 

P.S. My first thought for solving this was for a getMostRecentChild() method so you could have access to startTime() and duration() for that tween... now THAT would have been a verbose way to handle it :)

  • Like 3
Link to comment
Share on other sites

Hi guys :)

 

i m agree with Jack ;

when we have the power of nested timelines , delay/gaps and labels ; why we need any more method for timing !?

but in this discuss , i think the tl.chainMode() method makes more sense to me , that's limit and clear in timelines and not confused about timing with a large sequence , if i understand correctly , seems the tl.chainMode() with toggle ill work same as adding a new nested timeline that start at end !!...

anyway i think the chainmode should be a good trick .

Link to comment
Share on other sites

How about:

tl.appendMode("recent"); //always ignores infinite repeats, but honors other repeats
tl.appendMode("end");  //default

And also, we could add this:

tl.recent; //always points to the most recently added child tween/timeline/call

Or "recentChild" if you think that's more clear, but then maybe we'd have to change appendMode("recentChild") for consistency. 

 

Lastly, perhaps we should add a new endTime() method that allows you to grab the end time of any animation (according to its parent timeline's local time), so that you could do this:

tl.to(e, 1, {x:100}, tl.recent.endTime() - 0.5); //works even if appendMode is "end"

The down side: this is adding kb. Not vast amounts, of course. Which of these do you guys see this as worthwhile additions? 

  • Like 1
Link to comment
Share on other sites

I think this is quite clear and good

tl.appendMode("recent"); //always ignores infinite repeats, but honors other repeats
tl.appendMode("end");  //default

tl.recent seems good as is. Not sure its worth adding "Child" to it. 

 

 

endTime() can certainly be handy too.

Link to comment
Share on other sites

Hi Diaco,

 

hmm, I don't really understand the suggestion. Perhaps if you could explain the situation it solves and show some code (like Jamie did) that clearly illustrates how it would be used. I think I'm confused by the fact that we don't have a label() method anywhere nor do I know why its attached to something called add. I'm sure its just a typo or misunderstanding.

 

Are you referring to how the add() method can add a label like

 

timeline.add("someLabel") ? 

Link to comment
Share on other sites

Hi Carl :)

 

i mean ; if we have " END " option for position parameter when define a labe like this :  tl.add( "someLabel" , "END" ) , and every tween with that label play at end of timeline , i think this way is simplest and makes more sense .

and as Jack said ignores infinite repeats, but honors other repeats .

in this way we can manipulating the timing too easily , something like this :

tl.to(elem1, 20, {...} )
 .to(elem2, 4, {...} , "+=2" )
 .addLabel("endLebel", "END") // relative to the last tween 
 .to(elem1, 2, {...}, "endLebel")
 .to(elem2, 2, {...}, "endLebel+=2") // relative to the previous instance with " endLebel " label

,

or without relation to label as option for delay/gaps :

 .to(elem1, 2, {...}, tl.END )

,

i mean , maybe it`s better to staying away from adding new API and pay to improve the existing methods/codes ...

Link to comment
Share on other sites

Hey, 

 

+1 to this:

 

How about:

tl.appendMode("recent"); //always ignores infinite repeats, but honors other repeats
tl.appendMode("end");  //default

And also, we could add this:

tl.recent; //always points to the most recently added child tween/timeline/call

Or "recentChild" if you think that's more clear, but then maybe we'd have to change appendMode("recentChild") for consistency. 

 

Lastly, perhaps we should add a new endTime() method that allows you to grab the end time of any animation (according to its parent timeline's local time), so that you could do this:

tl.to(e, 1, {x:100}, tl.recent.endTime() - 0.5); //works even if appendMode is "end"

The down side: this is adding kb. Not vast amounts, of course. Which of these do you guys see this as worthwhile additions? 

 

Just a couple of questions though.

 

First, my knuckle-head is having some issues regarding tl.recent. This property refers to the latest added instance of the timeline regardless it's position, or the timeline's last child in it's playhead?

 

Two, appendMode() shouldn't have any effect on the values returned by startTime(), right?

Link to comment
Share on other sites

+1 for appendMode() .. like you posted above Jack:

tl.appendMode("recent"); //always ignores infinite repeats, but honors other repeats
tl.appendMode("recentChild"); //always points to the most recently added child tween/timeline/call
tl.appendMode("end");  //default:

:

I like the way Rodrigo was applying appendMode (chainMode) in his post above. . it made more sense, for me at least, to be in the chain!
 

It took me awhile to catch up and read this great topic, my eyes are cross eyed, but it was worth it :blink:

Link to comment
Share on other sites

In my own projects, I don't expect I'll be changing the appendMode at all, but it does seem to be a good solution if you want to place long or infinite tweens before others rather than use labels or reorder your javascript. I can't wait to see the messes people come to us with after they start using it ;)

 

I do wonder though, if the default hadn't been to append to the duration this entire time would I still be so hesitant towards this method?

 

In regards to the term recent; how about using the more definite newest or latest? (and you're right - it doesn't need Child after it)

Link to comment
Share on other sites

Are you saying you don't see enough up side to these additions to justify the potential for confusion and additional questions we'll need to field? It's a valuable question to ponder. 

 

As for recent/newest/latest, that's a tough one. "latest" initially seemed right to me, but then I realized that "late" has two different meanings, and some people might take that to mean "late" time-wise, as in latest chronologically that is scheduled in the timeline. "recent" seems more natural than "newest" (to me at least). Anyone else want to chime in? 

Link to comment
Share on other sites

My thoughts on recent was that recent is a bit indefinite; most recent is what we're implying, and newest/latest would perhaps be clearer. You're right about latest having two possible meanings though. So recent/newest... I can't decide.
 
Ok time for another mega post  ;)
 
I think you have to consider what issues are actually being solved with this, and whether any existing solutions worked well enough. As I understand it the only issue that this serves at the moment is situations where a long or infinite repeating tween is used early in the timeline breaks the usefulness of relative positions e.g.

tl.to(foo, 1, {repeat:-1, ...})
  // more tweens here (bar start at 2, baz start at 3)
// Solution 1 (exists) - absolute positions
tl.to(foo, 1, {repeat:-1, ...})
  .to(bar, 1, {...}, 2)
  .to(baz, 1, {...}, 3);
// Solution 2 (exists) - labels
tl.add("start")
  .to(foo, 1, {repeat:-1, ...})
  .to(bar, 1, {...}, "start+=2")
  .to(baz, 1, {...}, "start+=3");
// Solution 3 (exists) - reorganising
tl.add("foo_here")
  .to(bar, 1, {...}, "+=2")
  .to(baz, 1, {...})
  .to(foo, 1, {repeat:-1, ...}, "foo_here");
// Solution 4 (new) - appendMode
tl.appendMode('recent')
  .to(foo, 1, {repeat:-1, ...})
  .to(bar, 1, {...}, "+=1") // you mentioned ignoring infinite repeats, but I'm not sure that's entirely intuitive
  .to(baz, 1, {...});
// Solution 5 (new) - recent
tl.to(foo, 1, {repeat:-1, ...})
  .to(bar, 1, {...}, tl.recent.startTime() + 2)
  .to(baz, 1, {...}, tl.recent.endTime());

Again we should try to imagine these issues at larger scales, but we don't really have any real world test cases to work with yet. Suffice to say I don't think the original solutions were bad at all. And, the more I think about the new appendMode, the less useful I see it being. Consider my side effects post from earlier - do you think that this is a good solution for making a timeline in that style, or if a bit of reordering/use of existing APIs would have served just as well?
 
In my opinion the use of recent.endTime() is very clear and deliberate, and requires no further consideration to the position parameter. Sure it's more verbose, but I'd question whether it would get that much use that people would care about typing it often (no ctrl+c?). And recent.startTime() makes it easier to insert 2 tweens at the same point without more position calculation. That's something appendMode() doesn't help with.
 
With the new mode you would (assuming it was on) have to adapt to a new style of timeline creation, making sure your tweens are added in a very specific order and (if you're timeline code is long) remembering if the new mode is on or off. Would making modifications to a timeline built this way still be intuitive?
 
My other worry would be regard to new users. We are all very familiar with the position parameter as it exists, so it seems quite easy for us to understand how these changes are intended to work. A new user won't be this way. In the midst of learning how timelines are created and populated, they may be looking at examples and demos using appendMode('recent') (which I think is less intuitive and confusing) right when we want them to be focused on how simple GSAP can be to use. A position parameter that behaves in vastly different ways depending on which example you're looking at is not simple.
 
Anyway my question is this: It seems like most of you are quite keen on these APIs, especially appendMode(). Are you just agreeing that these new APIs could work and make sense to you, or do you expect that you will get good use from them in places you've struggled before?
 
 
I'll finish up with an edge case to consider

var tween = TweenLite.to(foo, 1, {...});

tl.appendMode("recent")
  .to(bar, 100, {...})
  .to(baz, 1, {...}, 0)
  .add(tween);

tween.kill();

// does this append to baz now, or duration()?
tl.to(foo, 1, {...});
 
// OR, is recent baz now, or null?
tl.to(foo, 1, {...}, tl.recent.endTime());

 
 
tl;dr I think tl.recent could be useful and at least it's intuitive. appendMode() not so much.

  • Like 3
Link to comment
Share on other sites

Great job, Jamie. 

 

I think Jamie proposed the core of my reply #5 in a much better way in this one paragraph:

 

My other worry would be regard to new users. We are all very familiar with the position parameter as it exists, so it seems quite easy for us to understand how these changes are intended to work. A new user won't be this way. In the midst of learning how timelines are created and populated, they may be looking at examples and demos using appendMode('recent') (which I think is less intuitive and confusing) right when we want them to be focused on how simple GSAP can be to use. A position parameter that behaves in vastly different ways depending on which example you're looking at is not simple.

 

This change could be added to the API today, I'd understand it perfectly and probably only use it in very rare situations. However the overhead it adds to the learning curve is really quite extreme. We already have a 13-minute video explaining position (which repeatedly states certain elements are added relative to the end of the timeline) and 5 demos. The whole point of those demos was to make position (which is already very flexible and as such a bit overwhelming) easy to understand.

 

I'm just having a hard time expecting a new user to understand all that and then add a caveat that says "But if  you set appendMode("recent"), then the behavior changes to blah blah blah". And in order to explain appendMode("recent") properly you have to illustrate why its needed which may bring users to assume there are problems with the default appendMode(). It all gets very tricky fast.

 

---

 

To comment on Jamie's solutions (which I really appreciate the clarity of and weren't intended to be best solutions).

 

I think solution 1 (absolute) and solution 2 (relative) actually illustrate the problem that in these situations (long or repeating tween first) we lose the natural flow / sequencing of newly added tweens. One of the big selling points of timelines is that you don't have to monkey with changing a bunch of "offset values" when you change the duration of one of the tweens in the timeline. I'd much rather recommend that in cases where you have a really long tween first and then want to build an overlapping sequence you just use a nested timeline

 

tl.to(foo, 1, {repeat:-1, ...})
  .add(myNested, 2);

myNested.to(foo, 1, {...})
.to(bar, 1, {...})
.to(baz, 1, {...})

---

via Jamie:

my question is this: It seems like most of you are quite keen on these APIs, especially appendMode(). Are you just agreeing that these new APIs could work and make sense to you, or do you expect that you will get good use from them in places you've struggled before?

 

I'm glad you asked. I am agreeing that the new APIs, especially appendMode(), could technically work and the way Jack presented them regarding naming and usage are solid and make sense.

 

I do not expect that I will get a ton of use from them. If appendMode() exists. Yes, I would use it instead of a nested timeline as it is a bit easier. However, I still struggle with it being the best thing for the API for new users.

 

I like the idea of tl.recent to grab the most recent child and endTime() method and do feel like they are something I could make more practical use of and they would not burden new users.

 

-- 

 

as for newest vs recent vs latest. I liked recent when I first heard it and still prefer it. 

 

--

 

Rodrigo, 

recent would refer to tween to the order in which the tween was added, not related to where it is in the timeline. So if you place 500 tweens in a timeline chained together and then add a new tween at time(0), that new tween, number 501 would still be "recent". 

 

And I don't think appendMode() would effect startTime(). appendMode() only effects where things go relative to other tweens in the timeline. Once a tween (or child) is in a timeline... its startTime() is virtually set in stone. The only way its startTime() will change is if you reference the tween and tell it to move or use shiftChildren().

 

 

 

 

 

 

  • Like 1
Link to comment
Share on other sites

I put a lot of stock in what you guys say here - thanks for all the thoughtful responses.

 

I agree with the overwhelming majority of what has been said. I see the liabilities of introducing appendMode(), especially with our existing articles/docs. The struggle for me has been that I think that in general, appendMode("recent") is probably what most people would prefer as the default behavior but we can't go backward now. Think about how you build a timeline now - don't you typically think "do this...then that...then that..."? And your animations are generally dropped in with that order in mind but sometimes we can't know what happened earlier, so Jamie's re-ordering solution won't always work. Luckily nested timelines and/or labels can solve just about any problem. I just think this new mode would be very intuitive and convenient.

 

That being said, it makes me nervous to expand the API unless we're absolutely SURE it is a very wise move. It's VERY difficult to contract an API once it has expanded. Well, it's impossible to do without the risk of breaking legacy code. Seeing as how we have gone for years with the existing timeline API with great success and people generally seem to love it, I don't feel compelled to take this risk right now by adding appendMode(). 

 

So we're left with "recent" and "endTime()". I think there's merit in both. One question: should "recent" be a property or a method, "recent()"? The benefit of making it a method is that it delivers extra flexibility so that we can run code internally before returning the result. I don't see a need for that right now, but I suppose it may arise in the future. For example, in Jamie's edge case where a tween is added and then immediately killed, should we somehow return a different tween for recent? I do NOT want to maintain an array internally of each child in the order it was added - that'd be slow and heavy (every time a tween is killed, it'd have to be gutted from that array for example). What's much cleaner in my view is to merely keep one reference to the last animation that was added...period. If you kill it, that's your problem - recent would still point to that tween that you just killed because it was indeed the most recent tween that was added. My point here is that making recent() a method would allow us flexibility to handle edge cases like that however we please whereas if it's a property, it's faster/easier to implement but limits flexibility. 

 

What do you guys prefer? If we're making it a method, do you prefer to rename it to something like recentChild() or getRecent() or getLastChildAdded()?

 

Right now, my gut says to go with timeline.recent() because it's concise and flexible. 

Link to comment
Share on other sites

Cool, this seems to sit much better with me. If being absolutely sure something is better is the ultimate litmus test, then I think leaving appendMode() out is best for the moment.

 

I'm all for making recent() a method as for the reasons you described.

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