aos-modern 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/js/aos.js ADDED
@@ -0,0 +1,354 @@
1
+ // Helpers
2
+ import detect from './helpers/detector';
3
+ import prepare from './helpers/prepare';
4
+ import elements from './helpers/elements';
5
+
6
+ /**
7
+ * Private variables
8
+ */
9
+ let $aosElements = [];
10
+ let initialized = false;
11
+ let observers = [];
12
+ let mutationObserver = null;
13
+ let resizeListener = null;
14
+ let orientationListener = null;
15
+ let loadListener = null;
16
+ let scrollListener = null;
17
+
18
+ /**
19
+ * Default options
20
+ */
21
+ let options = {
22
+ offset: 120,
23
+ delay: 0,
24
+ easing: 'ease',
25
+ duration: 400,
26
+ disable: false,
27
+ once: false,
28
+ startEvent: 'DOMContentLoaded',
29
+ throttleDelay: 99,
30
+ debounceDelay: 50,
31
+ disableMutationObserver: false,
32
+ };
33
+
34
+ const debounce = (fn, delay) => {
35
+ let timeout;
36
+ return (...args) => {
37
+ clearTimeout(timeout);
38
+ timeout = setTimeout(() => fn(...args), delay);
39
+ };
40
+ };
41
+
42
+ const throttle = (fn, delay) => {
43
+ let timer = null;
44
+ return (...args) => {
45
+ if (timer) return;
46
+ timer = setTimeout(() => {
47
+ fn(...args);
48
+ timer = null;
49
+ }, delay);
50
+ };
51
+ };
52
+
53
+ const cleanObservers = () => {
54
+ observers.forEach(obs => obs.disconnect());
55
+ observers = [];
56
+ };
57
+
58
+ const checkPendingElements = () => {
59
+ const scrollY = window.scrollY || window.pageYOffset;
60
+ const windowHeight = window.innerHeight;
61
+
62
+ $aosElements.forEach(el => {
63
+ const node = el.node;
64
+ if (node.classList.contains('aos-init') && !node.classList.contains('aos-animate')) {
65
+ const offsetAttr = node.getAttribute('data-aos-offset');
66
+ const offsetVal = (offsetAttr && !isNaN(offsetAttr)) ? parseInt(offsetAttr, 10) : options.offset;
67
+ const nodeHeight = node.offsetHeight;
68
+
69
+ const isInside = (scrollY + windowHeight >= el.position) &&
70
+ (scrollY <= el.position - offsetVal + nodeHeight);
71
+ if (isInside) {
72
+ node.classList.add('aos-animate');
73
+ }
74
+ }
75
+ });
76
+ };
77
+
78
+ const setupObservers = (elementsList, globalOptions) => {
79
+ cleanObservers();
80
+
81
+ elementsList.forEach(el => {
82
+ const node = el.node;
83
+
84
+ // Read attributes or fallback to options
85
+ const onceAttr = node.getAttribute('data-aos-once');
86
+ const once = onceAttr ? (onceAttr === 'true') : globalOptions.once;
87
+
88
+ const offsetAttr = node.getAttribute('data-aos-offset');
89
+ const offsetVal = (offsetAttr && !isNaN(offsetAttr)) ? parseInt(offsetAttr, 10) : globalOptions.offset;
90
+
91
+ const anchorSelector = node.getAttribute('data-aos-anchor');
92
+ const anchorEl = anchorSelector ? (document.querySelector(anchorSelector) || node) : node;
93
+
94
+ const placement = node.getAttribute('data-aos-anchor-placement') || globalOptions.anchorPlacement || 'top-bottom';
95
+
96
+ // Calculate vertical offset relative to viewport bottom
97
+ const windowHeight = window.innerHeight;
98
+ const nodeHeight = anchorEl.offsetHeight;
99
+ let Y = offsetVal;
100
+
101
+ switch (placement) {
102
+ case 'top-bottom':
103
+ break;
104
+ case 'center-bottom':
105
+ Y += nodeHeight / 2;
106
+ break;
107
+ case 'bottom-bottom':
108
+ Y += nodeHeight;
109
+ break;
110
+ case 'top-center':
111
+ Y += windowHeight / 2;
112
+ break;
113
+ case 'bottom-center':
114
+ Y += windowHeight / 2 + nodeHeight;
115
+ break;
116
+ case 'center-center':
117
+ Y += windowHeight / 2 + nodeHeight / 2;
118
+ break;
119
+ case 'top-top':
120
+ Y += windowHeight;
121
+ break;
122
+ case 'bottom-top':
123
+ Y += nodeHeight + windowHeight;
124
+ break;
125
+ case 'center-top':
126
+ Y += nodeHeight / 2 + windowHeight;
127
+ break;
128
+ }
129
+
130
+ const observer = new IntersectionObserver((entries) => {
131
+ entries.forEach(entry => {
132
+ const scrollY = window.scrollY || window.pageYOffset;
133
+ const isCurrentlyInside = (scrollY + windowHeight >= el.position) &&
134
+ (scrollY <= el.position - Y + nodeHeight);
135
+
136
+ if (entry.isIntersecting) {
137
+ if (isCurrentlyInside) {
138
+ node.classList.add('aos-animate');
139
+ if (once) {
140
+ observer.unobserve(anchorEl);
141
+ }
142
+ }
143
+ } else {
144
+ if (!once && !isCurrentlyInside) {
145
+ node.classList.remove('aos-animate');
146
+ }
147
+ }
148
+ });
149
+ }, {
150
+ rootMargin: `9999px 0px -${Y}px 0px`,
151
+ threshold: 0
152
+ });
153
+
154
+ observer.observe(anchorEl);
155
+ observers.push(observer);
156
+ });
157
+ };
158
+
159
+ /**
160
+ * Refresh AOS
161
+ */
162
+ const refresh = (initialize = false) => {
163
+ // Allow refresh only when it was first initialized on startEvent
164
+ if (initialize) initialized = true;
165
+
166
+ if (initialized) {
167
+ $aosElements = prepare($aosElements, options);
168
+ setupObservers($aosElements, options);
169
+ checkPendingElements();
170
+ return $aosElements;
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Hard refresh
176
+ */
177
+ const refreshHard = () => {
178
+ $aosElements = elements();
179
+ refresh();
180
+ };
181
+
182
+ /**
183
+ * Disable AOS
184
+ */
185
+ const disable = () => {
186
+ cleanObservers();
187
+ $aosElements.forEach(el => {
188
+ el.node.removeAttribute('data-aos');
189
+ el.node.removeAttribute('data-aos-easing');
190
+ el.node.removeAttribute('data-aos-duration');
191
+ el.node.removeAttribute('data-aos-delay');
192
+ el.node.classList.remove('aos-animate');
193
+ });
194
+ };
195
+
196
+ /**
197
+ * Destroy AOS (Cleanup all listeners and observers for SPA frameworks)
198
+ */
199
+ const destroy = () => {
200
+ cleanObservers();
201
+
202
+ if (resizeListener) {
203
+ window.removeEventListener('resize', resizeListener);
204
+ resizeListener = null;
205
+ }
206
+ if (orientationListener) {
207
+ window.removeEventListener('orientationchange', orientationListener);
208
+ orientationListener = null;
209
+ }
210
+ if (loadListener) {
211
+ window.removeEventListener('load', loadListener);
212
+ loadListener = null;
213
+ }
214
+ if (scrollListener) {
215
+ window.removeEventListener('scroll', scrollListener);
216
+ scrollListener = null;
217
+ }
218
+ if (mutationObserver) {
219
+ mutationObserver.disconnect();
220
+ mutationObserver = null;
221
+ }
222
+
223
+ initialized = false;
224
+ };
225
+
226
+ /**
227
+ * Check if AOS should be disabled
228
+ */
229
+ const isDisabled = (optionDisable) => {
230
+ return optionDisable === true ||
231
+ (optionDisable === 'mobile' && detect.mobile()) ||
232
+ (optionDisable === 'phone' && detect.phone()) ||
233
+ (optionDisable === 'tablet' && detect.tablet()) ||
234
+ (typeof optionDisable === 'function' && optionDisable() === true);
235
+ };
236
+
237
+ /**
238
+ * Initializing AOS
239
+ */
240
+ const init = (settings) => {
241
+ options = Object.assign(options, settings);
242
+ $aosElements = elements();
243
+
244
+ // Detect not supported browsers (<=IE9)
245
+ const browserNotSupported = document.all && !window.atob;
246
+
247
+ // Disable if needed
248
+ if (isDisabled(options.disable) || browserNotSupported) {
249
+ disable();
250
+ return $aosElements;
251
+ }
252
+
253
+ // Set global variables on body
254
+ const body = document.querySelector('body');
255
+ if (body) {
256
+ body.setAttribute('data-aos-easing', options.easing);
257
+ body.setAttribute('data-aos-duration', options.duration);
258
+ body.setAttribute('data-aos-delay', options.delay);
259
+ }
260
+
261
+ // Setup startEvent
262
+ if (options.startEvent === 'DOMContentLoaded' &&
263
+ ['complete', 'interactive'].indexOf(document.readyState) > -1) {
264
+ refresh(true);
265
+ } else if (options.startEvent === 'load') {
266
+ window.addEventListener(options.startEvent, () => {
267
+ refresh(true);
268
+ });
269
+ } else {
270
+ document.addEventListener(options.startEvent, () => {
271
+ refresh(true);
272
+ });
273
+ }
274
+
275
+ // Always register a window load event to ensure positions are recalculated after full asset loads
276
+ if (options.startEvent !== 'load') {
277
+ if (loadListener) {
278
+ window.removeEventListener('load', loadListener);
279
+ }
280
+ loadListener = () => {
281
+ refresh();
282
+ };
283
+ window.addEventListener('load', loadListener);
284
+ }
285
+
286
+ // Window resize listeners (cleanup if already exists)
287
+ if (resizeListener) {
288
+ window.removeEventListener('resize', resizeListener);
289
+ }
290
+ if (orientationListener) {
291
+ window.removeEventListener('orientationchange', orientationListener);
292
+ }
293
+
294
+ resizeListener = debounce(() => refresh(), options.debounceDelay);
295
+ orientationListener = debounce(() => refresh(), options.debounceDelay);
296
+
297
+ window.addEventListener('resize', resizeListener);
298
+ window.addEventListener('orientationchange', orientationListener);
299
+
300
+ if (!scrollListener) {
301
+ scrollListener = throttle(checkPendingElements, 100);
302
+ window.addEventListener('scroll', scrollListener);
303
+ }
304
+
305
+ // MutationObserver setup (cleanup if already exists)
306
+ if (mutationObserver) {
307
+ mutationObserver.disconnect();
308
+ mutationObserver = null;
309
+ }
310
+
311
+ if (!options.disableMutationObserver) {
312
+ const MutationObserverClass = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
313
+ if (MutationObserverClass) {
314
+ mutationObserver = new MutationObserverClass((mutations) => {
315
+ let shouldRefresh = false;
316
+ mutations.forEach(mutation => {
317
+ const allNodes = [
318
+ ...Array.from(mutation.addedNodes),
319
+ ...Array.from(mutation.removedNodes)
320
+ ];
321
+
322
+ const hasAosNode = allNodes.some(node => {
323
+ if (node.nodeType !== 1) return false;
324
+ if (node.hasAttribute('data-aos')) return true;
325
+ if (node.querySelector('[data-aos]')) return true;
326
+ return false;
327
+ });
328
+
329
+ if (hasAosNode) {
330
+ shouldRefresh = true;
331
+ }
332
+ });
333
+
334
+ if (shouldRefresh) {
335
+ refreshHard();
336
+ }
337
+ });
338
+
339
+ mutationObserver.observe(document.documentElement, {
340
+ childList: true,
341
+ subtree: true
342
+ });
343
+ }
344
+ }
345
+
346
+ return $aosElements;
347
+ };
348
+
349
+ export default {
350
+ init,
351
+ refresh,
352
+ refreshHard,
353
+ destroy
354
+ };
@@ -0,0 +1,76 @@
1
+ import getOffset from './../libs/offset';
2
+
3
+ /**
4
+ * Calculate offset
5
+ * basing on element's settings like:
6
+ * - anchor
7
+ * - offset
8
+ *
9
+ * @param {Node} el [Dom element]
10
+ * @return {number} [Final offset that will be used to trigger animation in good position]
11
+ */
12
+ const calculateOffset = (el, optionalOffset) => {
13
+ let elementOffsetTop = 0;
14
+ let additionalOffset = 0;
15
+ const windowHeight = window.innerHeight;
16
+ const attrs = {
17
+ offset: el.getAttribute('data-aos-offset'),
18
+ anchor: el.getAttribute('data-aos-anchor'),
19
+ anchorPlacement: el.getAttribute('data-aos-anchor-placement')
20
+ };
21
+
22
+ if (attrs.offset && !isNaN(attrs.offset)) {
23
+ additionalOffset = parseInt(attrs.offset, 10);
24
+ }
25
+
26
+ if (attrs.anchor) {
27
+ try {
28
+ const anchorEl = document.querySelector(attrs.anchor);
29
+ if (anchorEl) {
30
+ el = anchorEl;
31
+ }
32
+ } catch (e) {
33
+ console.warn(`AOS: "${attrs.anchor}" is not a valid selector`, e);
34
+ }
35
+ }
36
+
37
+ elementOffsetTop = getOffset(el).top;
38
+
39
+ switch (attrs.anchorPlacement) {
40
+ case 'top-bottom':
41
+ // Default offset
42
+ break;
43
+ case 'center-bottom':
44
+ elementOffsetTop += el.offsetHeight / 2;
45
+ break;
46
+ case 'bottom-bottom':
47
+ elementOffsetTop += el.offsetHeight;
48
+ break;
49
+ case 'top-center':
50
+ elementOffsetTop += windowHeight / 2;
51
+ break;
52
+ case 'bottom-center':
53
+ elementOffsetTop += windowHeight / 2 + el.offsetHeight;
54
+ break;
55
+ case 'center-center':
56
+ elementOffsetTop += windowHeight / 2 + el.offsetHeight / 2;
57
+ break;
58
+ case 'top-top':
59
+ elementOffsetTop += windowHeight;
60
+ break;
61
+ case 'bottom-top':
62
+ elementOffsetTop += el.offsetHeight + windowHeight;
63
+ break;
64
+ case 'center-top':
65
+ elementOffsetTop += el.offsetHeight / 2 + windowHeight;
66
+ break;
67
+ }
68
+
69
+ if (!attrs.anchorPlacement && !attrs.offset && !isNaN(optionalOffset)) {
70
+ additionalOffset = optionalOffset;
71
+ }
72
+
73
+ return elementOffsetTop + additionalOffset;
74
+ };
75
+
76
+ export default calculateOffset;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Device detector
3
+ */
4
+
5
+ const fullNameRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
6
+ const prefixRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
7
+ const fullNameMobileRe = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
8
+ const prefixMobileRe = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i;
9
+
10
+ const ua = () => navigator.userAgent || navigator.vendor || window.opera || '';
11
+
12
+ class Detector {
13
+ phone() {
14
+ const a = ua();
15
+ return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
16
+ }
17
+
18
+ mobile() {
19
+ const a = ua();
20
+ const isMobileUA = !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
21
+ const isIPadOS = navigator.maxTouchPoints > 0 && a.indexOf('Macintosh') !== -1;
22
+ return isMobileUA || isIPadOS;
23
+ }
24
+
25
+ tablet() {
26
+ const a = ua();
27
+ const isTabletUA = this.mobile() && !this.phone();
28
+ const isIPadOS = navigator.maxTouchPoints > 0 && a.indexOf('Macintosh') !== -1;
29
+ return isTabletUA || isIPadOS;
30
+ }
31
+ }
32
+
33
+ export default new Detector();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generate initial array with elements as objects
3
+ * This array will be extended later with elements attributes values
4
+ * like 'position'
5
+ */
6
+ const createArrayWithElements = (elements) => {
7
+ const nodeList = elements || document.querySelectorAll('[data-aos]');
8
+ return Array.from(nodeList, node => ({ node }));
9
+ };
10
+
11
+ export default createArrayWithElements;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Set or remove aos-animate class
3
+ * @param {node} el element
4
+ * @param {int} top scrolled distance
5
+ * @param {void} once
6
+ */
7
+ const setState = function (el, top, once) {
8
+ const attrOnce = el.node.getAttribute('data-aos-once');
9
+
10
+ if (top > el.position) {
11
+ el.node.classList.add('aos-animate');
12
+ } else if (typeof attrOnce !== 'undefined') {
13
+ if (attrOnce === 'false' || (!once && attrOnce !== 'true')) {
14
+ el.node.classList.remove('aos-animate');
15
+ }
16
+ }
17
+ };
18
+
19
+
20
+ /**
21
+ * Scroll logic - add or remove 'aos-animate' class on scroll
22
+ *
23
+ * @param {array} $elements array of elements nodes
24
+ * @param {bool} once plugin option
25
+ * @return {void}
26
+ */
27
+ const handleScroll = function ($elements, once) {
28
+ const scrollTop = window.pageYOffset;
29
+ const windowHeight = window.innerHeight;
30
+ /**
31
+ * Check all registered elements positions
32
+ * and animate them on scroll
33
+ */
34
+ $elements.forEach((el, i) => {
35
+ setState(el, windowHeight + scrollTop, once);
36
+ });
37
+ };
38
+
39
+ export default handleScroll;
@@ -0,0 +1,11 @@
1
+ import calculateOffset from './calculateOffset';
2
+
3
+ const prepare = ($elements, options) => {
4
+ $elements.forEach((el) => {
5
+ el.node.classList.add('aos-init');
6
+ el.position = calculateOffset(el.node, options.offset);
7
+ });
8
+ return $elements;
9
+ };
10
+
11
+ export default prepare;
@@ -0,0 +1,61 @@
1
+ let callback = () => {};
2
+
3
+ function containsAOSNode(nodes) {
4
+ let i, currentNode, result;
5
+
6
+ for (i = 0; i < nodes.length; i += 1) {
7
+ currentNode = nodes[i];
8
+
9
+ if (currentNode.dataset && currentNode.dataset.aos) {
10
+ return true;
11
+ }
12
+
13
+ result = currentNode.children && containsAOSNode(currentNode.children);
14
+
15
+ if (result) {
16
+ return true;
17
+ }
18
+ }
19
+
20
+ return false;
21
+ }
22
+
23
+ function getMutationObserver() {
24
+ return window.MutationObserver ||
25
+ window.WebKitMutationObserver ||
26
+ window.MozMutationObserver;
27
+ }
28
+
29
+ function isSupported() {
30
+ return !!getMutationObserver();
31
+ }
32
+
33
+ function ready(selector, fn) {
34
+ const doc = window.document;
35
+ const MutationObserver = getMutationObserver();
36
+
37
+ const observer = new MutationObserver(check);
38
+ callback = fn;
39
+
40
+ observer.observe(doc.documentElement, {
41
+ childList: true,
42
+ subtree: true,
43
+ removedNodes: true
44
+ });
45
+ }
46
+
47
+ function check(mutations) {
48
+ if (!mutations) return;
49
+
50
+ mutations.forEach(mutation => {
51
+ const addedNodes = Array.prototype.slice.call(mutation.addedNodes);
52
+ const removedNodes = Array.prototype.slice.call(mutation.removedNodes);
53
+ const allNodes = addedNodes.concat(removedNodes);
54
+
55
+ if (containsAOSNode(allNodes)) {
56
+ return callback();
57
+ }
58
+ });
59
+ }
60
+
61
+ export default { isSupported, ready };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Get offset of DOM element Helper
3
+ * including these with translation
4
+ *
5
+ * @param {Node} el [DOM element]
6
+ * @return {Object} [top and left offset]
7
+ */
8
+ const offset = (el) => {
9
+ let x = 0;
10
+ let y = 0;
11
+
12
+ while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
13
+ x += el.offsetLeft - (el.tagName !== 'BODY' ? el.scrollLeft : 0);
14
+ y += el.offsetTop - (el.tagName !== 'BODY' ? el.scrollTop : 0);
15
+ el = el.offsetParent;
16
+ }
17
+
18
+ return {
19
+ top: y,
20
+ left: x
21
+ };
22
+ };
23
+
24
+ export default offset;
@@ -0,0 +1,18 @@
1
+ import { useEffect } from 'react';
2
+ import AOS from '../js/aos';
3
+
4
+ /**
5
+ * React Hook to initialize and manage AOS lifecycle.
6
+ *
7
+ * @param {Object} options - AOS initialization options
8
+ */
9
+ export function useAOS(options) {
10
+ useEffect(() => {
11
+ AOS.init(options);
12
+ return () => {
13
+ AOS.destroy();
14
+ };
15
+ }, [options]);
16
+ }
17
+
18
+ export default useAOS;