]> BookStack Code Mirror - bookstack/blob - resources/assets/js/services/animations.js
Merge branch 'v0.26'
[bookstack] / resources / assets / js / services / animations.js
1 /**
2  * Fade out the given element.
3  * @param {Element} element
4  * @param {Number} animTime
5  * @param {Function|null} onComplete
6  */
7 export function fadeOut(element, animTime = 400, onComplete = null) {
8     animateStyles(element, {
9         opacity: ['1', '0']
10     }, animTime, () => {
11         element.style.display = 'none';
12         if (onComplete) onComplete();
13     });
14 }
15
16 /**
17  * Hide the element by sliding the contents upwards.
18  * @param {Element} element
19  * @param {Number} animTime
20  */
21 export function slideUp(element, animTime = 400) {
22     const currentHeight = element.getBoundingClientRect().height;
23     const computedStyles = getComputedStyle(element);
24     const currentPaddingTop = computedStyles.getPropertyValue('padding-top');
25     const currentPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
26     const animStyles = {
27         height: [`${currentHeight}px`, '0px'],
28         overflow: ['hidden', 'hidden'],
29         paddingTop: [currentPaddingTop, '0px'],
30         paddingBottom: [currentPaddingBottom, '0px'],
31     };
32
33     animateStyles(element, animStyles, animTime, () => {
34         element.style.display = 'none';
35     });
36 }
37
38 /**
39  * Show the given element by expanding the contents.
40  * @param {Element} element - Element to animate
41  * @param {Number} animTime - Animation time in ms
42  */
43 export function slideDown(element, animTime = 400) {
44     element.style.display = 'block';
45     const targetHeight = element.getBoundingClientRect().height;
46     const computedStyles = getComputedStyle(element);
47     const targetPaddingTop = computedStyles.getPropertyValue('padding-top');
48     const targetPaddingBottom = computedStyles.getPropertyValue('padding-bottom');
49     const animStyles = {
50         height: ['0px', `${targetHeight}px`],
51         overflow: ['hidden', 'hidden'],
52         paddingTop: ['0px', targetPaddingTop],
53         paddingBottom: ['0px', targetPaddingBottom],
54     };
55
56     animateStyles(element, animStyles, animTime);
57 }
58
59 /**
60  * Used in the function below to store references of clean-up functions.
61  * Used to ensure only one transitionend function exists at any time.
62  * @type {WeakMap<object, any>}
63  */
64 const animateStylesCleanupMap = new WeakMap();
65
66 /**
67  * Animate the css styles of an element using FLIP animation techniques.
68  * Styles must be an object where the keys are style properties, camelcase, and the values
69  * are an array of two items in the format [initialValue, finalValue]
70  * @param {Element} element
71  * @param {Object} styles
72  * @param {Number} animTime
73  * @param {Function} onComplete
74  */
75 function animateStyles(element, styles, animTime = 400, onComplete = null) {
76     const styleNames = Object.keys(styles);
77     for (let style of styleNames) {
78         element.style[style] = styles[style][0];
79     }
80
81     const cleanup = () => {
82         for (let style of styleNames) {
83             element.style[style] = null;
84         }
85         element.style.transition = null;
86         element.removeEventListener('transitionend', cleanup);
87         if (onComplete) onComplete();
88     };
89
90     setTimeout(() => {
91         requestAnimationFrame(() => {
92             element.style.transition = `all ease-in-out ${animTime}ms`;
93             for (let style of styleNames) {
94                 element.style[style] = styles[style][1];
95             }
96
97             if (animateStylesCleanupMap.has(element)) {
98                 const oldCleanup = animateStylesCleanupMap.get(element);
99                 element.removeEventListener('transitionend', oldCleanup);
100             }
101
102             element.addEventListener('transitionend', cleanup);
103             animateStylesCleanupMap.set(element, cleanup);
104         });
105     }, 10);
106 }