3

I'm trying to get my head around the web animations standard and their polyfill, as I saw it work nicely in the Angular animations library (you set an animation end value to '*' and this becomes 100% of the div size, but that uses a special Angular Animations DSL).

I thought I would start with something simple, so all I want to do is expand a div from 0 height to 'auto'. i know there are thousands of other ways to do this, but I was trying to use web-animations-js with this code

The code below (which resembles an MDN example) causes the div to expand directly to 'auto' but after a 1 second delay, whereas I want it smoothly to expand.

let formDiv = document.querySelector("#new-present-form");
formDiv.animate([
    { height: 0},
    { height: 'auto'}
], {
    easing: 'ease-in-out',
    duration: 1000,
    fill: 'forwards'    // ensures menu stays open at end of animation
})

By contrast this

formDiv.animate({
    height: [0, '100%'],
    easing: 'ease-in-out'
}, {
    duration: 1000,
    fill: 'forwards'    // ensures menu stays open at end of animation
})

causes the div to expand immediately, but again with no smooth transition.

This does give a smooth transition, but requires a carefully chosen value to replace '300px' and is precisely what I want to avoid.

formDiv.animate([
    { "height": '0px', offset: 0},
    { "height": '300px', offset: 1}
], {
    duration: 1000,
    iterations: 1,
    fill: 'forwards'    // ensure form stays open at end of animation
})
Simon H
  • 20,332
  • 14
  • 71
  • 128

2 Answers2

1

Unfortunately, it is not directly possible to do this with Web Animations or CSS animations/transitions yet. That is because CSS does not have a means to represent an intermediate state between an 'auto' length and a fixed length. The proposal to fix this involves allowing 'auto' inside calc(), e.g. calc(auto + 36px). You can follow the progress of this development on the CSS Transitions Github issue.

In the interim, many people have been able to work around this by animating max-height instead. For example, if you expect your 'auto' height to be somewhere between 300px and 600px, then you could leave height as 'auto', and animate max-height from '0' to '700px'. It's not perfect, but for a short animation it's often close enough.

Alternatively, one could set the height to auto, get the used value of the height using getComputedStyle, and, supposing it returned 375px, create an animation from, height: '0' to height: '375px'. If you do not specify a fill on the animation then when it completes, the computed value of the height will switch from height: 375px to height: auto (which should make no visual difference but mean that the element's height responds to future changes in the content size).

Regarding the error about partial keyframes, that is a short-term issue where both Firefox and Chrome have not shipped support for omitting the first or last keyframe. Both Firefox and Chrome have implemented this feature and it should ship this year, however, it still won't fix this issue until auto is permitted in calc().

Update 22 May with (completely untested) code samples as requested:

// Option 1: Use max-height
formDiv.animate(
  { maxHeight: ['0', '700px'] },
  { easing: 'ease-in-out', duration: 1000 }
);

// Option 2: Use used height
//
// (Beware, flushes layout initially so will probably not be very performant.)
formDiv.style.height = 'auto';
const finalHeight = getComputedStyle(formDiv).height;
formDiv.style.height = '0';
formDiv.animate(
  { height: ['0', finalHeight] },
  { easing: 'ease-in-out', duration: 1000 }
);

Of course if you don't actually have anything below the div you might be able to get away with a transform animation which will definitely be the most performant.

// Option 3: Use scale-y transform
formDiv.style.transformOrigin = '50% 0%';
formDiv.animate(
  { transform: ['scaleY(0)', 'scaleY(1)'] },
  { easing: 'ease-in-out', duration: 1000 }
);
brianskold
  • 809
  • 5
  • 10
-4

If height is not mandatory you can try giving padding to the div.

@keyframes example {
   from{padding:0px;}
    to{padding: 50px;}
}

Vote up if it was help full.

foxenn_pro
  • 71
  • 9