UNPKG

animate-height

Version:

Animate the height of an element to 'auto' or '0px'.

114 lines (111 loc) 4.12 kB
var AnimationState; (function (AnimationState) { AnimationState[AnimationState["None"] = 0] = "None"; AnimationState[AnimationState["Auto"] = 1] = "Auto"; AnimationState[AnimationState["Zero"] = 2] = "Zero"; })(AnimationState || (AnimationState = {})); const defaultOptions = { duration: 300, timing: 'ease', }; class Animator { constructor(el) { this.state = AnimationState.None; this.orgTransition = ''; this.el = el; this.el.addEventListener('transitionend', this); } /** * Animate the height of the element to 'auto'. * * @param options - Animation options * @returns The height of the element after the animation completes. */ autoHeight({ duration, timing } = defaultOptions) { return new Promise(res => { const { el } = this; const style = getComputedStyle(el); duration = duration || defaultOptions.duration; this.orgTransition = style.transition; const transition = [ this.orgTransition, `height ${duration}ms ${timing || defaultOptions.timing}`.trim(), ].filter(t => t).join(', '); this.state = AnimationState.Auto; el.style.transition = transition; el.style.height = style.height; const scrollHeight = `${el.scrollHeight}px`; requestAnimationFrame(() => el.style.height = scrollHeight); const start = performance.now(); requestAnimationFrame(function awaitCompletion() { const height = getComputedStyle(el).height; if ((performance.now() - start) < duration && height !== scrollHeight) { requestAnimationFrame(awaitCompletion); return; } res(height ? parseInt(height.replace(/px/, ''), 10) : 0); }); }); } /** * Animate the height of the element to '0px'. * * @param options - Animation options * @param timing - CSS transition timing function */ zeroHeight({ duration, timing } = defaultOptions) { return new Promise(res => { const { el } = this; const style = getComputedStyle(el); this.orgTransition = style.transition; const transition = [ this.orgTransition, `height ${duration || defaultOptions.duration}ms ${timing || defaultOptions.timing}`.trim(), ].filter(t => t).join(', '); this.state = AnimationState.Zero; el.style.transition = transition; el.style.height = style.height; requestAnimationFrame(() => el.style.height = '0'); requestAnimationFrame(function awaitCompletion() { let height = getComputedStyle(el).height; height = height ? parseInt(height.replace(/px/, ''), 10) : 0; if (height > 0) { requestAnimationFrame(awaitCompletion); return; } res(); }); }); } handleEvent(ev) { switch (ev.type) { case 'transitionend': this.onTransitionEnded(); break; } } onTransitionEnded() { if (this.state === AnimationState.Auto) { this.el.style.height = 'auto'; } this.el.style.transition = this.orgTransition; this.orgTransition = ''; this.state = AnimationState.None; } } /** * Animate the height of an element to 'auto'. * * @param el - The element to animate * @param options - Animation options * @returns The height of the element after the animation completes. */ const autoHeight = (el, options) => new Animator(el).autoHeight(options); /** * Animate the height of an element to '0px'. * * @param el - The element to animate * @param options - Animation options */ const zeroHeight = (el, options) => new Animator(el).zeroHeight(options); export { Animator as HeightAnimator, autoHeight, zeroHeight };