julianshapiro / velocity

Accelerated JavaScript animation.
VelocityJS.org
MIT License
17.29k stars 1.56k forks source link

[ TESTERS WANTED ] Velocity UI Pack Feature: "animateParentHeight" #161

Closed julianshapiro closed 8 years ago

julianshapiro commented 10 years ago

The UI pack has a new (and currently undocumented) option entitled animateParentHeight.

First, update your copies of Velocity and the UI pack.

Second, simply pass the animateParentHeight property into a UI pack effect call to have the targeted elements' containing element expand/contract vertically as the animating children are transitioned in/out of view. (animateParentHeight can naturally only be used with transition effects -- whose names end in In/Out.)

$elements.velocity("transition.bounceIn", { animateParentHeight: true });

Example CodePen: http://codepen.io/julianshapiro/pen/wrAqI/ . You can play around with stagger and backwards options.

Prior to this feature existing, transitioning elements that were inside a containing element would cause the container's height to jump in discrete steps (equal to the targeted elements' individual heights) instead of fluidly animate. (This same problem naturally occurs with every other animation library.)

I need you guys to test this out in your own environments/use cases and point out any issues before I finalize the feature and document it. Note: Do not implement this in production environments yet. If this feature turns out to be buggy, it may very well be dropped altogether.

tsriram commented 10 years ago

Yeah, it is working now. I wish it animates the siblings too smoothly. Check this out - http://codepen.io/anon/pen/HecCl

Sibling divs jump up & down.

kokarn commented 10 years ago

@julianshapiro Great! Now it works wonders.

The only thing I notice now is that it looks kinda funky when all the paragraphs scale out simultaneously but without any connection to the parents animation. (Wow, that was hard to describe... Hopefully you understand what I mean.)

marcselman commented 10 years ago

@tsriram Easy, just add a wrapper around your child element: http://codepen.io/anon/pen/Dytip


I've created a little demo: http://codepen.io/anon/pen/oDgnv

Things I've noticed:

julianshapiro commented 10 years ago

The only thing I notice now is that it looks kinda funky when all the paragraphs scale out simultaneously but without any connection to the parents animation.

@julianshapiro Works pretty good so far. But it looks like the animateParentHeight animation takes longer than the Out animation.

The upcoming update tweaks the timing a bit. It's unlikely I'll be tweaking it further. (It's not easy to find a timing multiplier that perfectly works for both stagger and non-stagger child animations.)

In general, this is why I'm wary of implementing this feature. There is so much potential for confusion.

The demo looks so awesome when just clicking the buttons like a madman ;)

Lol. Yes it does.

Notes to self:

dillinghamio commented 10 years ago

'animateParentHeight' could maybe be a little more concise, although verbose may more appropriate for expanding upon the convention moving forward. Just offering some ideas.

$(".child").velocity("transition.expandOut", { tidy: true }) // it cleans up after itself
$(".child").velocity("transition.expandOut", { tidy: 'slideUp' }) // choose the animation
$(".child").velocity("transition.expandOut", { parent: 'slideUp' }) // or even 'parent'
dminkovsky commented 10 years ago

Hey @julianshapiro, took a second to look at this feature and the UI pack for the first time. Haven't had a chance to use it in my projects, but I've played with the various Pens out there. GREAT STUFF.

My question is this: given things like animateParentHeight, do you see the UI pack components moving away from hard-wired values? Way back in the day when I first started learning about JS/CSS3 animation, it was because Animate.css was all hard wired with -10000px translations, etc., which is not just hacky but more importantly looks awful and canned. For me, the value of the UI pack would not so much be things like CSS state cleanup, but height/width value calculations of the sort that animateParentHeight provides. Generalizing these behaviors to UI pack the components would be killer. Does that makes sense?

julianshapiro commented 10 years ago

Hey @dminkovsky! Thank you for the kind words, good sir :)

As for hard-wiring, you can register your own (or even re-register existing) effects using the UI pack. See the bottom section of the UI pack pane on the Velocity docs. With that feature, the world is your oyster :)

As for more features that deal with helpful UI calculation/manipulation, I'm actually very interested in providing. If you or anyone else can provide some strong ideas, I'm all ears.

jamesreggio commented 10 years ago

Hey @julianshapiro, I broke your thing.

http://codepen.io/jamesreggio/pen/jEDmB http://codepen.io/jamesreggio/pen/Fkihr http://codepen.io/jamesreggio/pen/erlnJ

For the benefit of others, @julianshapiro and I spoke about this in-real-life. While I absolutely agree that there is value in this feature, I think the only way to robustly calculate the differential in height is to have the browser lay out the final state, measure it, then restore the initial state and tween between the two.

Things become event more challenging if you support width in addition to height. (I think this is totally within reach, though, and would be happy to test the corner-cases if you try your hand at an implementation.)

Things become mind-numbingly complex if you support swapping elements (i.e., adding some elements at the same time you're removing others).

This feature overlaps with some of the issues I've been exploring with @ef4 in ef4/liquid-fire#49. liquid-fire supports both height and width, as well as incoming and outgoing simultaneously. The approach we took to solve these measurement problems is roughly this:

  1. Measure the parent before animation begins (this is your before state)
  2. Measure and set an explicit size on the outgoing elements, and make them absolutely positioned
  3. Make the incoming elements invisible (with visibility: hidden), and Insert them into the parent
  4. Measure the parent again (this is your after state)
  5. Measure and set an explicit size on the incoming elements, and make them absolutely positioned
  6. Transition between before and after on the parent element's height and width
  7. Simultaneously, perform the incoming and outgoing animations, being sure to set visibility: visible on the incoming elements
  8. When animations complete, remove the outgoing elements and statically-position the incoming elements (also cleanup the explicit dimensions from all elements)

Box-sizing models and other complexities lurk, and there's no guarantee that the reflows involved here won't cause a flicker, but IMHO, it's the best possible approach because it allows the browser to calculate the dimensions. (It's worth noting that we haven't encountered any known flickering from this yet.)

julianshapiro commented 10 years ago

Hey @jamesreggio,

I think the only way to robustly calculate the differential in height is to have the browser lay out the final state, measure it, then restore the initial state and tween between the two.

Agreed. Further, I agree that your suggested implementation is likely the best approach. To integrate it would necessitate dropping the current animateParentHeight API (which is currently an option exposed by the UI pack) and instead provide a global function (that will also be exposed by the UI pack), e.g. a "shift" function as follows:

$.Velocity.shift(
    parentElement,
    duration, // The durations specified in the options objects below are ignored
    { outgoingElements, outgoingPropertiesMap, outgoingOptions },
    { incomingElements, incomingPropertiesMap, incomingOptions }
);

If we can figure out the right API, I will go ahead and try my hand at implementing this as performantly as possible. Any thoughts on this syntax?

Thanks so much!

franciscolourenco commented 9 years ago

This looks awesome. Would be nice to be able to use the one object notation as well to make it more explicit and readable, specially since the minimum use case for this function is by default complex.

$.Velocity.shift({
    parentElement: parentElement,
    duration: 1000, // The durations specified in the options objects below are ignored
    outgoing: { elements, propertiesMap, options },
    incoming: { elements, propertiesMap, options }
});

Velocity.mutate would be yet another option for the name.

franciscolourenco commented 9 years ago

padding + border-box = parent too tall http://jsbin.com/xakipigino/1/edit?html,css,js,output

Rycochet commented 8 years ago

Padding is not taken into account with border-box any more