Skip to content

Commit 7cf9eff

Browse files
committed
Added support for any scrolling container (window or other)
1 parent cc9ff01 commit 7cf9eff

File tree

1 file changed

+84
-58
lines changed

1 file changed

+84
-58
lines changed

src/scripts/main.js

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import parseUnit from 'parse-unit'
22
import eases from 'eases'
33

4-
const instances = []
4+
const scrollContainers = []
55
const isBrowser = typeof window !== 'undefined'
66

77
/**
@@ -29,9 +29,9 @@ const debounce = function(fn, duration) {
2929
* @param {Array} instances
3030
* @returns {Array} instances - Active instances.
3131
*/
32-
const getActiveInstances = function(instances) {
32+
const getActiveInstances = function(container) {
3333

34-
return instances.filter((instance) => instance != null && instance.isActive())
34+
return container.instances.filter((instance) => instance != null && instance.isActive())
3535

3636
}
3737

@@ -40,21 +40,22 @@ const getActiveInstances = function(instances) {
4040
* @param {Array} instances
4141
* @returns {Array} instances - Tracked instances.
4242
*/
43-
const getTrackedInstances = function(instances) {
43+
const getTrackedInstances = function(container) {
4444

45-
return instances.filter((instance) => instance != null && instance.getData().track)
45+
return container.instances.filter((instance) => instance != null && instance.getData().track)
4646

4747
}
4848

4949

5050
/**
5151
* Returns the number of scrolled pixels.
52+
* @param {Object} scrollingElement - Scrolling container.
5253
* @returns {Number} scrollTop
5354
*/
54-
const getScrollTop = function() {
55+
const getScrollTop = function(scrollingElement) {
5556

5657
// Use scrollTop because it's faster than getBoundingClientRect()
57-
return (document.scrollingElement || document.documentElement).scrollTop
58+
return scrollingElement.scrollTop
5859

5960
}
6061

@@ -127,12 +128,12 @@ const mapDirectToProperty = function(direct, properties) {
127128
* Converts a relative value to an absolute value.
128129
* @param {String} value
129130
* @param {Node} elem - Anchor of the relative value.
130-
* @param {?Integer} scrollTop - Pixels scrolled in document.
131+
* @param {Object} scrollingElement - Scrolling container.
131132
* @param {?Integer} viewportHeight - Height of the viewport.
132133
* @returns {String} value - Absolute value.
133134
*/
134-
const relativeToAbsoluteValue = function(value, elem, scrollTop = getScrollTop(), viewportHeight = getViewportHeight()) {
135-
135+
const relativeToAbsoluteValue = function(value, elem, scrollingElement, viewportHeight = getViewportHeight()) {
136+
const scrollTop = getScrollTop(scrollingElement)
136137
const elemSize = elem.getBoundingClientRect()
137138

138139
const elemAnchor = value.match(/^[a-z]+/)[0]
@@ -157,7 +158,7 @@ const relativeToAbsoluteValue = function(value, elem, scrollTop = getScrollTop()
157158
* @param {?Object} data
158159
* @returns {Object} data - Validated data.
159160
*/
160-
const validate = function(data = {}) {
161+
const validate = function(data = {}, scrollingElement) {
161162

162163
// Copy root object to avoid changes by reference
163164
data = Object.assign({}, data)
@@ -184,8 +185,8 @@ const validate = function(data = {}) {
184185

185186
} else {
186187

187-
if (isRelativeValue(data.from) === true) data.from = relativeToAbsoluteValue(data.from, data.elem)
188-
if (isRelativeValue(data.to) === true) data.to = relativeToAbsoluteValue(data.to, data.elem)
188+
if (isRelativeValue(data.from) === true) data.from = relativeToAbsoluteValue(data.from, data.elem, scrollingElement)
189+
if (isRelativeValue(data.to) === true) data.to = relativeToAbsoluteValue(data.to, data.elem, scrollingElement)
189190

190191
}
191192

@@ -224,12 +225,13 @@ const validate = function(data = {}) {
224225
/**
225226
* Calculates the props of an instance.
226227
* @param {Object} instance
227-
* @param {?Integer} scrollTop - Pixels scrolled in document.
228+
* @param {Object} scrollingElement - Scrolling container.
228229
* @returns {Object} Calculated props and the element to apply styles to.
229230
*/
230-
const getProps = function(instance, scrollTop = getScrollTop()) {
231-
232-
const data = instance.getData()
231+
const getProps = function(instance) {
232+
const data = instance.getData()
233+
234+
const scrollTop = getScrollTop(instance.scrollingElement)
233235

234236
// 100% in pixel
235237
const total = data.to.value - data.from.value
@@ -322,49 +324,44 @@ const setProps = function(elem, props) {
322324
* Gets and sets new props when the user has scrolled and when there are active instances.
323325
* This part get executed with every frame. Make sure it's performant as hell.
324326
* @param {Object} style - Style object.
325-
* @param {?Integer} previousScrollTop
326327
* @returns {?*}
327328
*/
328-
const loop = function(style, previousScrollTop) {
329+
const loop = function(style) {
329330

330331
// Continue loop
331332
const repeat = () => {
332-
333-
// It depends on the browser, but it turns out that closures
334-
// are sometimes faster than .bind or .apply.
335-
requestAnimationFrame(() => loop(style, previousScrollTop))
336-
333+
scrollContainers.forEach(container => {
334+
// console.log(1)
335+
// Get all active instances
336+
337+
const activeInstances = getActiveInstances(container)
338+
339+
if (activeInstances.length) {
340+
const scrollTop = getScrollTop(container.element)
341+
342+
if (container.scrollTop !== scrollTop) {
343+
activeInstances
344+
.map((instance) => getProps(instance))
345+
.forEach(({ elem, props }) => setProps(elem, props))
346+
347+
container.previousScrollTop = scrollTop
348+
}
349+
}
350+
})
351+
requestAnimationFrame(repeat)
337352
}
338-
339-
// Get all active instances
340-
const activeInstances = getActiveInstances(instances)
341-
342-
// Only continue when active instances available
343-
if (activeInstances.length === 0) return repeat()
344-
345-
const scrollTop = getScrollTop()
346-
347-
// Only continue when scrollTop has changed
348-
if (previousScrollTop === scrollTop) return repeat()
349-
else previousScrollTop = scrollTop
350-
351-
// Get and set new props of each instance
352-
activeInstances
353-
.map((instance) => getProps(instance, scrollTop))
354-
.forEach(({ elem, props }) => setProps(elem, props))
355-
356-
repeat()
357-
353+
requestAnimationFrame(repeat)
358354
}
359355

360356
/**
361357
* Creates a new instance.
362358
* @param {Object} data
363359
* @returns {Object} instance
364360
*/
365-
export const create = function(data) {
366-
361+
export const create = function(data, scrollingElement=null) {
367362
// Store the parsed data
363+
if (!scrollingElement) scrollingElement = (document.scrollingElement || document.documentElement)
364+
const _scrollingElement = scrollingElement
368365
let _data = null
369366

370367
// Store if instance is started or stopped
@@ -387,13 +384,13 @@ export const create = function(data) {
387384
// Parses and calculates data
388385
const _calculate = function() {
389386

390-
_data = validate(data)
387+
_data = validate(data, _scrollingElement)
391388

392389
}
393390

394391
// Update props
395392
const _update = () => {
396-
393+
397394
// Get new props
398395
const { elem, props } = getProps(instance)
399396

@@ -423,13 +420,17 @@ export const create = function(data) {
423420

424421
// Replace instance instead of deleting the item to avoid
425422
// that the index of other instances changes.
426-
instances[index] = undefined
427-
423+
scrollContainers.forEach(container => {
424+
if (container.element == scrollingElement) {
425+
container.instances[index] = undefined
426+
}
427+
})
428428
}
429429

430430
// Assign instance to a variable so the instance can be used
431431
// elsewhere in the current function.
432432
const instance = {
433+
scrollingElement: scrollingElement,
433434
isActive: _isActive,
434435
getData: _getData,
435436
calculate: _calculate,
@@ -440,8 +441,28 @@ export const create = function(data) {
440441
}
441442

442443
// Store instance in global array and save the index
443-
const index = instances.push(instance) - 1
444-
444+
let index = 0
445+
446+
// Store the scroll container to get their value
447+
let found = false
448+
scrollContainers.forEach(container => {
449+
if (container.element == scrollingElement) {
450+
index = container.instances.push(instance) - 1
451+
found = true
452+
}
453+
})
454+
if (!found) {
455+
scrollContainers.push({
456+
element: scrollingElement,
457+
instances: [
458+
instance
459+
],
460+
previousScrollTop: 0
461+
})
462+
}
463+
464+
console.log(scrollContainers)
465+
445466
// Calculate data for the first time
446467
instance.calculate()
447468

@@ -455,16 +476,21 @@ if (isBrowser === true) {
455476
// Start to loop
456477
loop()
457478

479+
console.log('okay')
480+
458481
// Recalculate and update instances when the window size changes
459482
window.addEventListener('resize', debounce(() => {
460483

461-
// Get all tracked instances
462-
const trackedInstances = getTrackedInstances(instances)
484+
scrollContainers.forEach(container => {
485+
486+
// Get all tracked instances
487+
const trackedInstances = getTrackedInstances(container)
463488

464-
trackedInstances.forEach((instance) => {
465-
instance.calculate()
466-
instance.update()
467-
})
489+
trackedInstances.forEach((instance) => {
490+
instance.calculate()
491+
instance.update()
492+
})
493+
})
468494

469495
}, 50))
470496

0 commit comments

Comments
 (0)