2 * Used in the function below to store references of clean-up functions.
3 * Used to ensure only one transitionend function exists at any time.
4 * @type {WeakMap<object, any>}
6 const animateStylesCleanupMap = new WeakMap();
9 * Fade out the given element.
10 * @param {Element} element
11 * @param {Number} animTime
12 * @param {Function|null} onComplete
14 export function fadeOut(element, animTime = 400, onComplete = null) {
15 cleanupExistingElementAnimation(element);
16 animateStyles(element, {
19 element.style.display = 'none';
20 if (onComplete) onComplete();
25 * Hide the element by sliding the contents upwards.
26 * @param {Element} element
27 * @param {Number} animTime
29 export function slideUp(element, animTime = 400) {
30 cleanupExistingElementAnimation(element);
31 const currentHeight = element.getBoundingClientRect().height;
32 const computedStyles = getComputedStyle(element);
33 const currentPaddingTop = computedStyles.getPropertyValue('padding-top');
34 const currentPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
36 height: [`${currentHeight}px`, '0px'],
37 overflow: ['hidden', 'hidden'],
38 paddingTop: [currentPaddingTop, '0px'],
39 paddingBottom: [currentPaddingBottom, '0px'],
42 animateStyles(element, animStyles, animTime, () => {
43 element.style.display = 'none';
48 * Show the given element by expanding the contents.
49 * @param {Element} element - Element to animate
50 * @param {Number} animTime - Animation time in ms
52 export function slideDown(element, animTime = 400) {
53 cleanupExistingElementAnimation(element);
54 element.style.display = 'block';
55 const targetHeight = element.getBoundingClientRect().height;
56 const computedStyles = getComputedStyle(element);
57 const targetPaddingTop = computedStyles.getPropertyValue('padding-top');
58 const targetPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
60 height: ['0px', `${targetHeight}px`],
61 overflow: ['hidden', 'hidden'],
62 paddingTop: ['0px', targetPaddingTop],
63 paddingBottom: ['0px', targetPaddingBottom],
66 animateStyles(element, animStyles, animTime);
70 * Animate the css styles of an element using FLIP animation techniques.
71 * Styles must be an object where the keys are style properties, camelcase, and the values
72 * are an array of two items in the format [initialValue, finalValue]
73 * @param {Element} element
74 * @param {Object} styles
75 * @param {Number} animTime
76 * @param {Function} onComplete
78 function animateStyles(element, styles, animTime = 400, onComplete = null) {
79 const styleNames = Object.keys(styles);
80 for (let style of styleNames) {
81 element.style[style] = styles[style][0];
84 const cleanup = () => {
85 for (let style of styleNames) {
86 element.style[style] = null;
88 element.style.transition = null;
89 element.removeEventListener('transitionend', cleanup);
90 animateStylesCleanupMap.delete(element);
91 if (onComplete) onComplete();
95 element.style.transition = `all ease-in-out ${animTime}ms`;
96 for (let style of styleNames) {
97 element.style[style] = styles[style][1];
100 element.addEventListener('transitionend', cleanup);
101 animateStylesCleanupMap.set(element, cleanup);
106 * Run the active cleanup action for the given element.
107 * @param {Element} element
109 function cleanupExistingElementAnimation(element) {
110 if (animateStylesCleanupMap.has(element)) {
111 const oldCleanup = animateStylesCleanupMap.get(element);