-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
106 lines (98 loc) · 2.98 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
function scrollFrame() {
let animationFrame = window.requestAnimationFrame(loop)
let callbackCollection = []
let lastY = -1
let lastX = -1
/**
* Object that holds a callback function and data about how to handle it
*
* @param {function} func The callback function
* @param {boolean} breakOnError If callback function throws an error, remove from scroll listener
*/
function Callback(func, breakOnError) {
this.func = func
this.breakOnError = breakOnError
}
/**
* Determines if scroll has occurred and callbacks exist to initiate a trigger
*/
function loop () {
// Only process checks if callback exists
if (callbackCollection.length) {
// Only process loop if we are scrolling
const y = window.pageYOffset
const x = window.pageXOffset
switch (false) {
case (y === lastY):
case (x === lastX):
lastY = y
lastX = x
trigger()
}
}
animationFrame = window.requestAnimationFrame(loop)
}
/**
* Fire all callback functions in the callback collection
*/
function trigger() {
// Reverse while loop is safer when potentially removing elements from array
let i=callbackCollection.length
while(i--) {
// callback functions are external and could be problematic
const callback = callbackCollection[i]
try {
callback.func()
} catch (err) {
if (callback.breakOnError) {
removeScrollListener(callback.func)
} else {
// pass through error otherwise
throw err
}
}
}
}
/**
* Test if callback collection contains a reference to the supplied callback function
*
* @param {function} func The function to test for presence in the collection
* @return {boolean} true if found, false if not found
*/
function contains(func) {
return callbackCollection.length && callbackCollection.reduce((prev, callback) => {
prev || func === callback.func
}, false)
}
/**
* Binds a callback function to the scroll listener
*
* @param {function} func The callback function to trigger on scroll
* @param {boolean} breakOnError If callback function throws an error, remove from scroll listener
*/
function addScrollListener(func, breakOnError = false) {
// Only allow a single instance of a callback function
if (!contains(func)) {
// Only allow functions as callbackCollection
if (typeof func === 'function') {
callbackCollection.push(new Callback(func, breakOnError))
}
}
}
/**
* Remove a callback function from the scroll listener
*
* @param {function} func The callback function to remove from the scroll listener
*/
function removeScrollListener(func) {
let i=callbackCollection.length
while(i--) {
if (callbackCollection[i].func === func) {
callbackCollection.splice(i, 1)
break
}
}
}
return { addScrollListener, removeScrollListener }
}
module.exports = scrollFrame()