2 * Hide the element by sliding the contents upwards.
3 * @param {Element} element
4 * @param {Number} animTime
6 export function slideUp(element, animTime = 400) {
7 const currentHeight = element.getBoundingClientRect().height;
8 const computedStyles = getComputedStyle(element);
9 const currentPaddingTop = computedStyles.getPropertyValue('padding-top');
10 const currentPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
12 height: [`${currentHeight}px`, '0px'],
13 overflow: ['hidden', 'hidden'],
14 paddingTop: [currentPaddingTop, '0px'],
15 paddingBottom: [currentPaddingBottom, '0px'],
18 animateStyles(element, animStyles, animTime, () => {
19 element.style.display = 'none';
24 * Show the given element by expanding the contents.
25 * @param {Element} element - Element to animate
26 * @param {Number} animTime - Animation time in ms
28 export function slideDown(element, animTime = 400) {
29 element.style.display = 'block';
30 const targetHeight = element.getBoundingClientRect().height;
31 const computedStyles = getComputedStyle(element);
32 const targetPaddingTop = computedStyles.getPropertyValue('padding-top');
33 const targetPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
35 height: ['0px', `${targetHeight}px`],
36 overflow: ['hidden', 'hidden'],
37 paddingTop: ['0px', targetPaddingTop],
38 paddingBottom: ['0px', targetPaddingBottom],
41 animateStyles(element, animStyles, animTime);
45 * Used in the function below to store references of clean-up functions.
46 * Used to ensure only one transitionend function exists at any time.
47 * @type {WeakMap<object, any>}
49 const animateStylesCleanupMap = new WeakMap();
52 * Animate the css styles of an element using FLIP animation techniques.
53 * Styles must be an object where the keys are style properties, camelcase, and the values
54 * are an array of two items in the format [initialValue, finalValue]
55 * @param {Element} element
56 * @param {Object} styles
57 * @param {Number} animTime
58 * @param {Function} onComplete
60 function animateStyles(element, styles, animTime = 400, onComplete = null) {
61 const styleNames = Object.keys(styles);
62 for (let style of styleNames) {
63 element.style[style] = styles[style][0];
66 const cleanup = () => {
67 for (let style of styleNames) {
68 element.style[style] = null;
70 element.style.transition = null;
71 element.removeEventListener('transitionend', cleanup);
72 if (onComplete) onComplete();
76 requestAnimationFrame(() => {
77 element.style.transition = `all ease-in-out ${animTime}ms`;
78 for (let style of styleNames) {
79 element.style[style] = styles[style][1];
82 if (animateStylesCleanupMap.has(element)) {
83 const oldCleanup = animateStylesCleanupMap.get(element);
84 element.removeEventListener('transitionend', oldCleanup);
87 element.addEventListener('transitionend', cleanup);
88 animateStylesCleanupMap.set(element, cleanup);