greensock / GSAP

GSAP (GreenSock Animation Platform), a JavaScript animation library for the modern web
https://gsap.com
19.56k stars 1.72k forks source link

Self-cleaning tweens #235

Closed fregante closed 6 years ago

fregante commented 6 years ago

Inline styles override regular styles, this is a problem when regular styles change via media query or :hover.

In this example you can see how I use a .from tween to transition in a couple elements: https://codepen.io/anon/pen/dVJoOG

Unfortunately gsap leaves behind all its inline styles and requires manual clearing of the tweened properties on each and every element. This becomes quite a task on longer timelines.

Would it be possible to give an option to .from tweens (and timelines) to automatically clear the props once the animation is done?

jackdoyle commented 6 years ago

You could definitely use clearProps: https://codepen.io/GreenSock/pen/5156a91fd138eb1f8c01fc46adf1b908/

The problems with just assuming that it's desirable to wipe away the inline styles at the end of a from() tween are:

  1. What if there were inline styles there to begin with, thus removing them at the end of the tween could cause a jarring shift?
  2. How would you handle overwriting or killing of the tween? Like what if it's halfway through and then another tween takes over - is it supposed to clear the properties then? It could get pretty tricky. Some might expect one behavior, others another.

Hopefully it's easy enough to chuck a clearProps at the end of your tween(s).

fregante commented 6 years ago

The problems with just assuming that it's desirable to wipe away the inline styles at the end of a from() tween are:

I'm not assuming, it can be an explicit option.

What if there were inline styles there to begin with, thus removing them at the end of the tween could cause a jarring shift?

One more reason to avoid clearProps and have gsap remember what value it was before it set the first style.

How would you handle overwriting or killing of the tween? Like what if it's halfway through and then another tween takes over - is it supposed to clear the properties then? It could get pretty tricky. Some might expect one behavior, others another.

Given that the cleaning would happen at the end, it's safe to assume that if the tween is interrupted, the end isn't reached, and the props aren't cleaned. I would assume that a restoreProps behavior would be implemented as additional set tweens at the end (or however clearProps is implemented in single tweens)

Hopefully it's easy enough to chuck a clearProps at the end of your tween(s).

That's what the demo includes (via button) but on long timelines with many props and elements it starts to get hairy.

I did have an automatic implementation of this auto-clearing behavior but I'm not sure if it works correctly:

function _clearProps(props, target) {
    // don't clear props if other tweens are still active on the element
    if (!TweenLite.getTweensOf(target).length) {
        TweenLite.set(target, {
            clearProps: props.join(','),
        });
    }
}

function clearProps (parent) {
    parent = this || parent;
    const tweens = parent.getChildren ? parent.getChildren() : [parent];
    const map = tweens.reduce((all, tween) => {
        const targets = Array.isArray(tween.target) ? tween.target : [tween.target];
        targets.forEach(target => {
            if (target && tween.vars.css) {
                const props = Object.keys(tween.vars.css);
                all.set(target, all.has(target) ? all.get(target).concat(props) : props);
            }
        });
        return all;
    }, new Map());
    map.forEach(_clearProps);
}
const tl = ...;
tl.add(() => clearProps(tl));
jackdoyle commented 6 years ago

So your goal is to not define clearProps on your tweens?

(By the way, you can use clearProps:"all" to clear away everything)

Instead, you want to write a function that'll rip through a TimelineLite/Max instance that you feed in and have it add a clearProps dynamically with just the right properties in there for each and every child tween, right?

fregante commented 6 years ago

(By the way, you can use clearProps:"all" to clear away everything)

Some inline styles have to be preserved, I often have background-color on some elements for example.

So your goal is to not define clearProps on your tweens?

The goal would be to have a clearProps: auto that:

This is preferable to:

fregante commented 6 years ago

Essentially, this is part of a thought I had about using GSAP for User Interfaces, where animations are used to transition between states and should not interfere with the UI after the transition is done.

Manually cleaning this interference makes gsap feel like it's not the right tool for the job (transitions), but rather just for animations. (well, "duh", right?)

jackdoyle commented 6 years ago

I'l consider this for a future release, but for now this function should do the trick (if I understand your goal correctly):

function clearProps(tl) {
    var children = tl.getChildren ? tl.getChildren(true, true, false) : [tl],
        i = children.length,
        tween, vars;
    while (--i > -1) {
        tween = children[i];
        vars = tween.vars.css || tween.vars;
        if (tween.vars.runBackwards) {
            vars.clearProps = Object.keys(vars).join(",");
        }
    }
}

Does that help?

fregante commented 6 years ago

What's tween.vars.runBackwards? Should it be !tween.vars.runBackwards?

jackdoyle commented 6 years ago

runBackwards:true is basically an indicator that it's a from() tween. I figured that you'd only want to run this logic (adding clearProps) on from() tweens, right? Was the script not working as expected?