forked from Stanko/animated-scroll-to
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanimated-scroll-to.js
157 lines (129 loc) · 5.05 KB
/
animated-scroll-to.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
(function() {
'use strict';
// desiredOffset - page offset to scroll to
// speed - duration of the scroll per 1000px
function __ANIMATE_SCROLL_TO(desiredOffset) {
var userOptions = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
if (desiredOffset instanceof HTMLElement) {
var scrollTop = window.scrollY || document.documentElement.scrollTop;
desiredOffset = scrollTop + desiredOffset.getBoundingClientRect().top;
}
var defaultOptions = {
speed: 500,
minDuration: 250,
maxDuration: 1500,
cancelOnUserAction: true,
element: window,
onComplete: undefined,
};
var options = {};
Object.keys(defaultOptions).forEach(function(key) {
options[key] = userOptions[key] ? userOptions[key] : defaultOptions[key];
});
options.isWindow = options.element === window;
var initialScrollPosition = null;
var maxScroll = null;
if (options.isWindow) {
// get cross browser scroll position
initialScrollPosition = window.scrollY || document.documentElement.scrollTop;
// cross browser document height minus window height
maxScroll = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
) - window.innerHeight;
} else {
// DOM element
initialScrollPosition = options.element.scrollTop;
maxScroll = options.element.scrollHeight - options.element.clientHeight;
}
// If the scroll position is greater than maximum available scroll
if (desiredOffset > maxScroll) {
desiredOffset = maxScroll;
}
// Calculate diff to scroll
var diff = desiredOffset - initialScrollPosition;
// Do nothing if the page is already there
if (diff === 0) {
// Execute callback if there is any
if (options.onComplete && typeof options.onComplete === 'function') {
options.onComplete()
}
return;
}
// Calculate duration of the scroll
var duration = Math.abs(Math.round((diff / 1000) * options.speed));
// Set minimum and maximum duration
if (duration < options.minDuration) {
duration = options.minDuration;
} else if (duration > options.maxDuration) {
duration = options.maxDuration;
}
var startingTime = Date.now();
// Request animation frame ID
var requestID = null;
// Method handler
var handleUserEvent = null;
if (options.cancelOnUserAction) {
// Set handler to cancel scroll on user action
handleUserEvent = function() { cancelAnimationFrame(requestID); };
window.addEventListener('keydown', handleUserEvent);
} else {
// Set handler to prevent user actions while scroll is active
handleUserEvent = function(e) { e.preventDefault(); };
window.addEventListener('scroll', handleUserEvent);
}
window.addEventListener('wheel', handleUserEvent);
window.addEventListener('touchstart', handleUserEvent);
var step = function () {
var timeDiff = Date.now() - startingTime;
var t = (timeDiff / duration) - 1;
var easing = t * t * t + 1;
var scrollPosition = Math.round(initialScrollPosition + (diff * easing));
if (timeDiff < duration && scrollPosition !== desiredOffset) {
// If scroll didn't reach desired offset or time is not elapsed
// Scroll to a new position
// And request a new step
if (options.isWindow) {
options.element.scrollTo(0, scrollPosition);
} else {
options.element.scrollTop = scrollPosition;
}
requestID = requestAnimationFrame(step);
} else {
// If the time elapsed or we reached the desired offset
// Set scroll to the desired offset (when rounding made it to be off a pixel or two)
// Clear animation frame to be sure
if (options.isWindow) {
options.element.scrollTo(0, desiredOffset);
} else {
options.element.scrollTop = desiredOffset;
}
cancelAnimationFrame(requestID);
// Remove listeners
window.removeEventListener('wheel', handleUserEvent);
window.removeEventListener('touchstart', handleUserEvent);
if (options.cancelOnUserAction) {
window.removeEventListener('keydown', handleUserEvent);
} else {
window.removeEventListener('scroll', handleUserEvent);
}
// Animation is complete, execute callback if there is any
if (options.onComplete && typeof options.onComplete === 'function') {
options.onComplete()
}
}
};
// Start animating scroll
requestID = requestAnimationFrame(step);
}
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
module.exports = __ANIMATE_SCROLL_TO;
exports = module.exports;
}
exports.default = __ANIMATE_SCROLL_TO;
} else if (window) {
window.animateScrollTo = __ANIMATE_SCROLL_TO;
}
}).call(this);