Jump to content
Search Community

Using greensock to change classes without tweening styles

jamiejefferson 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

First off I just want to say how much I'm enjoying the new javascript platform: it's certainly made a hard job easy :D

 

I'm using css:className for some of my tweens, and while it's working really well, it would be ideal in my situation to just have the timeline manage class changes but not actually animate the style transitions.

 

My use case:

I would like to change a few different styles of an element at once, hence the classes, but I don't necessarily want tranisition animations on all of the changed styles. I'm developing this for HTML5/EPUB only, so css transitions are fine to define these (simple color changes/fades etc). These tweens will be 'themeable' as well, so it's helpful to control as many of the visual changes/transitions as possible through css.

 

As far as I can tell this isn't directly provided at the moment (although I'd love to be proven wrong), and while I'm happy to roll my own solution to this I thought I'd see what the greensock community thought of this problem.

 

Something like the following would be nice (although I'm not sure what effect a duration other than 0 would have):

TweenLite.to(element, 0, {class:'+=newclass'});

 

...on a side note I've also found that tweening css:className with duration 0 doesn't quite work as advertised (if inserting it into a timeline it will run immediately, not at the correct point in the timeline) and I have to use a tiny duration like 0.01 for it to obey me. The culprit:

timeline.insert(TweenLite.to(element, 0, {css:{className:'+=newclass'}}), 5);
// triggers immediately, not at 5 seconds into timeline
timeline.insert(TweenLite.to(element, 0.01, {css:{className:'+=newclass'}}), 5);
// works as intended

Link to comment
Share on other sites

Guest newdevide

I dont think using "to" would be the best approach since you are using zero as duration. Why not try using "set"? So your script will lokk like...

 

TweenLite.set(element, {class:'+=newclass'});

 

So soory cant help you much, since i've never used "set" to add class. So i don't know if it is the sollution you're looking for

Link to comment
Share on other sites

Newdevide, thanks for helping!

 

Jamie,

 

If you want to use 0-duration tweens, set immediateRender:false in the vars

 

tl.append(TweenLite.to(element, 0, {css:{left:20}, immediateRender:false});

 

When using set(), the set() calls should wait until the right time to fire in the timeline. See this example:

 

http://jsfiddle.net/geekambassador/DMCJt/

 

if your sets are firing at the wrong time, please post some code and we will look at it.

Link to comment
Share on other sites

Yea I hadn't realised a zero duration tween would render immediately vs a tween that has a non-zero duration, although I suppose there are some use cases as to why that is a thing?

 

I don't have access to my work files over the weekend to run a test unfortunately, but I'll try the immediateRender value first thing next week. Not sure if this is what is expected, but in my case:

// changing
tl.insert(TweenLite.to(element, 0, {css:{className:'+=newclass'}}), 5);
// to
tl.insert(TweenLite.set(element, {css:{className:'+=newclass'}}), 5);
// = no difference - element is assigned newclass immediately

Playing around on jsfiddle suggests this isn't the case though so I will need to look into it. I guess I probably posted this question prematurely so I will do a thorough investigation of the greensock code before updating with more specifics on Monday.

Link to comment
Share on other sites

Yep, Carl is right about immediateRender:false and I figured I'd chime in to explain why that is. You'd be surprised how many people use TweenLite/Max to simply set values (rather than tweening them) in various situations. I think it's because the syntax is familiar and it allows them to rely on the engine to do its cross-browser magic (like setting opacity/scale/rotation/skew in a way that works even in older versions of IE). Anyway, they'll often use a zero-duration tween in their code and then immediately after that line, they perform some other action on the object assuming the change has already been made (like the tween finished) even though by default, tweens don't actually start rendering things until the next frame refresh. So it was a strategic decision - either the engine waits to render or it doesn't (can't have it both ways). Due to common practices of developers, the default behavior for zero-duration tweens is to render immediately. But again, you can alter that behavior with immediateRender:false.

 

You might be wondering "okay, fine, but if I'm putting it into a TimelineLite or TimelineMax, shouldn't it know to wait to render until that spot in the timeline?" The problem is that when a TweenLite or TweenMax instance is created, it is impossible for it to know what you're going to do with it (like place it in a timeline). Even if you're creating it inside an insert() or append() call, like append( TweenLite.to(...) ), the instance itself gets created first and then it gets inserted, so at the time of instantiation, it's impossible for it to jump outside itself and say "hey, is this guy inserting me into a timeline?..."

 

I hope that clears things up.

 

Oh, and by the way, using the set() convenience method of TimelineLite or TimelineMax automatically sets immediateRender:false for you, so you can do:

 

var tl = new TimelineLite();
tl.set(element, {...});

And in that case, it'll wait to render until that spot in the timeline.

Link to comment
Share on other sites

Thanks for the thorough explanation Jack. immediaterender on my Tweens has fixed the duration issue so now I'm just looking at the class changes.

 

Ideally I would like to use a Timeline to maintain class changes for an element, but without any parsing/tweening of the associated styles. e.g.

 

0s . . . . . 2s . . . . . . 4s

|------------[--------------]------------|

class='' . . class='on' . . class=''

 

Between 2s and 4s the timeline should make sure my element has the classname 'on', and outside of the time it should be removed. Since I'm just using a set / 0 duration tween to change the class, I don't need the tween to parse the class styles for differences; just edit the class attribute. Is this possible? Every bit of performance I can glean is helpful.

 

I figured that with a 0 duration tween it wouldn't be too much of an issue, but I'm getting the occassional flicker at the start of a css transition so maybe a frame of tweening is occassionally sneaking in?

 

I uploaded a short example of what I am seeing at http://jsfiddle.net/9bSvL/ (the timings are a bit 'off' to emphasize the flicker), although I only seem to get the flicker in Firefox.

Link to comment
Share on other sites

Something like this would probably perform best:

 

var target = $('#target')[0];
var highlight = $('#highlight')[0];
var timeline = new TimelineMax({paused:true});

timeline.call(addClass, [highlight, "on"], null, 1);
timeline.call(removeClass, [highlight, "on"], null, 1);

timeline.play();

function addClass(element, cls) {
   element.className += " " + cls;
}

function removeClass(element, cls) {
   element.className = element.className.split(cls).join("");
}

 

The slight glitch you were seeing has do to with the fact that in order to figure out what's different between the two classes and how they affect that particular object, CSSPlugin must temporarily apply the class to the object and check all the values to figure out what's different (and thus needs tweening). So since you had a transition applied to the css itself, it looks like the browser was incorrectly implementing it despite the fact that CSSPlugin immediately reset the className back to what it was. But the code above eliminates that issue, of course, since CSSPlugin isn't having to do anything at all.

 

Make sense?

Link to comment
Share on other sites

Yea I initially implemented this using callbacks, but I wanted to see if the Timeline could also be used to maintain the correct classes when jumping between different positions in the timeline. Jumping in between two add/remove callbacks loses the state since the addClass is skipped. CSSPlugin handles this nicely though (and in my case the fade-in css transition still runs, rather than jumping straight into full highlight, which makes things looks really neat).

 

I had considered trying to modify CSSPlugin to skip class-checking/tweening if the duration is 0, but that will have to be a project for another day. For now I'm content using CSSPlugin as my use of it is fairly light so I'm not seeing any adverse affects on performance at the moment.

Link to comment
Share on other sites

That's actually pretty easy to do, so I went ahead and implemented it in the latest version of CSSPlugin. When the tween has a duration of 0, it'll completely skip parsing the class altogether (and thus it doesn't need to temporarily apply the new one).

 

Enjoy!

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