Comparing version 1.4.4 to 2.0.0
@@ -19,10 +19,2 @@ (function (global, factory) { | ||
// public | ||
function select(selector) { | ||
if (selector instanceof Element) { return selector; } | ||
else if (typeof selector === 'string') | ||
{ return document.querySelector(selector); } | ||
return null; | ||
} | ||
function selectAll(selector, parent) { | ||
@@ -129,39 +121,47 @@ if ( parent === void 0 ) parent = document; | ||
function scrollama() { | ||
var ZERO_MOE = 1; // zero with some rounding margin of error | ||
var callback = {}; | ||
var OBSERVER_NAMES = [ | ||
'stepAbove', | ||
'stepBelow', | ||
'stepProgress', | ||
'viewportAbove', | ||
'viewportBelow' | ||
]; | ||
var cb = { | ||
stepEnter: function () {}, | ||
stepExit: function () {}, | ||
stepProgress: function () {} | ||
}; | ||
var io = {}; | ||
var containerEl = null; | ||
var graphicEl = null; | ||
var stepEl = null; | ||
var id = null; | ||
var stepEl = []; | ||
var stepOffsetHeight = []; | ||
var stepOffsetTop = []; | ||
var stepStates = []; | ||
var id = null; | ||
var offsetVal = 0; | ||
var offsetMargin = 0; | ||
var vh = 0; | ||
var ph = 0; | ||
var stepOffsetHeight = null; | ||
var stepOffsetTop = null; | ||
var bboxGraphic = null; | ||
var viewH = 0; | ||
var pageH = 0; | ||
var previousYOffset = 0; | ||
var progressThreshold = 0; | ||
var isReady = false; | ||
var isEnabled = false; | ||
var debugMode = false; | ||
var isDebug = false; | ||
var progressMode = false; | ||
var progressThreshold = 0; | ||
var preserveOrder = false; | ||
var triggerOnce = false; | ||
var stepStates = null; | ||
var containerState = null; | ||
var previousYOffset = -1; | ||
var direction = null; | ||
var direction = 'down'; | ||
var exclude = []; | ||
// HELPERS | ||
function generateId() { | ||
/*** HELPERS ***/ | ||
function generateInstanceID() { | ||
var a = 'abcdefghijklmnopqrstuv'; | ||
var l = a.length; | ||
var t = new Date().getTime(); | ||
var t = Date.now(); | ||
var r = [0, 0, 0].map(function (d) { return a[Math.floor(Math.random() * l)]; }).join(''); | ||
@@ -211,18 +211,19 @@ return ("" + r + t); | ||
function disconnectObserver(name) { | ||
if (io[name]) { io[name].forEach(function (d) { return d.disconnect(); }); } | ||
} | ||
function handleResize() { | ||
vh = window.innerHeight; | ||
ph = getPageHeight(); | ||
viewH = window.innerHeight; | ||
pageH = getPageHeight(); | ||
bboxGraphic = graphicEl ? graphicEl.getBoundingClientRect() : null; | ||
offsetMargin = offsetVal * viewH; | ||
offsetMargin = offsetVal * vh; | ||
if (isReady) { | ||
stepOffsetHeight = stepEl.map(function (el) { return el.offsetHeight; }); | ||
stepOffsetTop = stepEl.map(getOffsetTop); | ||
if (isEnabled) { updateIO(); } | ||
} | ||
stepOffsetHeight = stepEl ? stepEl.map(function (el) { return el.offsetHeight; }) : []; | ||
stepOffsetTop = stepEl ? stepEl.map(getOffsetTop) : []; | ||
if (isEnabled && isReady) { updateIO(); } | ||
if (debugMode) | ||
{ update({ id: id, stepOffsetHeight: stepOffsetHeight, offsetMargin: offsetMargin, offsetVal: offsetVal }); } | ||
if (isDebug) { update({ id: id, stepOffsetHeight: stepOffsetHeight, offsetMargin: offsetMargin, offsetVal: offsetVal }); } | ||
} | ||
@@ -234,12 +235,6 @@ | ||
isEnabled = true; | ||
} else if (!enable) { | ||
if (io.top) { io.top.disconnect(); } | ||
if (io.bottom) { io.bottom.disconnect(); } | ||
if (io.stepAbove) { io.stepAbove.forEach(function (d) { return d.disconnect(); }); } | ||
if (io.stepBelow) { io.stepBelow.forEach(function (d) { return d.disconnect(); }); } | ||
if (io.stepProgress) { io.stepProgress.forEach(function (d) { return d.disconnect(); }); } | ||
if (io.viewportAbove) { io.viewportAbove.forEach(function (d) { return d.disconnect(); }); } | ||
if (io.viewportBelow) { io.viewportBelow.forEach(function (d) { return d.disconnect(); }); } | ||
isEnabled = false; | ||
return true; | ||
} | ||
OBSERVER_NAMES.forEach(disconnectObserver); | ||
isEnabled = false; | ||
} | ||
@@ -257,3 +252,12 @@ | ||
// NOTIFY CALLBACKS | ||
/*** NOTIFY CALLBACKS ***/ | ||
function notifyStepProgress(element, progress) { | ||
var index = getIndex(element); | ||
if (progress !== undefined) { stepStates[index].progress = progress; } | ||
var resp = { element: element, index: index, progress: stepStates[index].progress }; | ||
if (stepStates[index].state === 'enter') { cb.stepProgress(resp); } | ||
} | ||
function notifyOthers(index, location) { | ||
@@ -264,7 +268,10 @@ if (location === 'above') { | ||
var ss = stepStates[i]; | ||
if (ss.state === 'enter') { notifyStepExit(stepEl[i], 'down'); } | ||
if (ss.direction === 'up') { | ||
if (ss.state !== 'enter' && ss.direction !== 'down') { | ||
notifyStepEnter(stepEl[i], 'down', false); | ||
notifyStepExit(stepEl[i], 'down'); | ||
} | ||
} else if (ss.state === 'enter') { notifyStepExit(stepEl[i], 'down'); } | ||
// else if (ss.direction === 'up') { | ||
// notifyStepEnter(stepEl[i], 'down', false); | ||
// notifyStepExit(stepEl[i], 'down'); | ||
// } | ||
} | ||
@@ -294,3 +301,2 @@ } else if (location === 'below') { | ||
stepStates[index].state = 'enter'; | ||
if (preserveOrder && check && direction === 'down') | ||
@@ -302,16 +308,9 @@ { notifyOthers(index, 'above'); } | ||
if ( | ||
callback.stepEnter && | ||
typeof callback.stepEnter === 'function' && | ||
!exclude[index] | ||
) { | ||
callback.stepEnter(resp, stepStates); | ||
if (debugMode) { notifyStep({ id: id, index: index, state: 'enter' }); } | ||
if (cb.stepEnter && !exclude[index]) { | ||
cb.stepEnter(resp, stepStates); | ||
if (isDebug) { notifyStep({ id: id, index: index, state: 'enter' }); } | ||
if (triggerOnce) { exclude[index] = true; } | ||
} | ||
if (progressMode) { | ||
if (direction === 'down') { notifyStepProgress(element, 0); } | ||
else { notifyStepProgress(element, 1); } | ||
} | ||
if (progressMode) { notifyStepProgress(element); } | ||
} | ||
@@ -323,2 +322,9 @@ | ||
if (progressMode) { | ||
if (direction === 'down' && stepStates[index].progress < 1) | ||
{ notifyStepProgress(element, 1); } | ||
else if (direction === 'up' && stepStates[index].progress > 0) | ||
{ notifyStepProgress(element, 0); } | ||
} | ||
// store most recent trigger | ||
@@ -328,104 +334,83 @@ stepStates[index].direction = direction; | ||
if (progressMode) { | ||
if (direction === 'down') { notifyStepProgress(element, 1); } | ||
else { notifyStepProgress(element, 0); } | ||
} | ||
if (callback.stepExit && typeof callback.stepExit === 'function') { | ||
callback.stepExit(resp, stepStates); | ||
if (debugMode) { notifyStep({ id: id, index: index, state: 'exit' }); } | ||
} | ||
cb.stepExit(resp, stepStates); | ||
if (isDebug) { notifyStep({ id: id, index: index, state: 'exit' }); } | ||
} | ||
function notifyStepProgress(element, progress) { | ||
var index = getIndex(element); | ||
var resp = { element: element, index: index, progress: progress }; | ||
if (callback.stepProgress && typeof callback.stepProgress === 'function') | ||
{ callback.stepProgress(resp); } | ||
} | ||
/*** OBSERVER - INTERSECT HANDLING ***/ | ||
// this is good for entering while scrolling down + leaving while scrolling up | ||
function intersectStepAbove(ref) { | ||
var entry = ref[0]; | ||
function notifyContainerEnter() { | ||
var resp = { direction: direction }; | ||
containerState.direction = direction; | ||
containerState.state = 'enter'; | ||
updateDirection(); | ||
var isIntersecting = entry.isIntersecting; | ||
var boundingClientRect = entry.boundingClientRect; | ||
var target = entry.target; | ||
// bottom = bottom edge of element from top of viewport | ||
// bottomAdjusted = bottom edge of element from trigger | ||
var top = boundingClientRect.top; | ||
var bottom = boundingClientRect.bottom; | ||
var topAdjusted = top - offsetMargin; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
// entering above is only when topAdjusted is negative | ||
// and bottomAdjusted is positive | ||
if ( | ||
callback.containerEnter && | ||
typeof callback.containerEnter === 'function' | ||
isIntersecting && | ||
topAdjusted <= 0 && | ||
bottomAdjusted >= 0 && | ||
direction === 'down' && | ||
ss.state !== 'enter' | ||
) | ||
{ callback.containerEnter(resp); } | ||
} | ||
{ notifyStepEnter(target, direction); } | ||
function notifyContainerExit() { | ||
var resp = { direction: direction }; | ||
containerState.direction = direction; | ||
containerState.state = 'exit'; | ||
if (callback.containerExit && typeof callback.containerExit === 'function') | ||
{ callback.containerExit(resp); } | ||
// exiting from above is when topAdjusted is positive and not intersecting | ||
if ( | ||
!isIntersecting && | ||
topAdjusted > 0 && | ||
direction === 'up' && | ||
ss.state === 'enter' | ||
) | ||
{ notifyStepExit(target, direction); } | ||
} | ||
// OBSERVER - INTERSECT HANDLING | ||
// this is good for entering while scrolling up + leaving while scrolling down | ||
function intersectStepBelow(ref) { | ||
var entry = ref[0]; | ||
// if TOP edge of step crosses threshold, | ||
// bottom must be > 0 which means it is on "screen" (shifted by offset) | ||
function intersectStepAbove(entries) { | ||
updateDirection(); | ||
entries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting; | ||
var boundingClientRect = entry.boundingClientRect; | ||
var target = entry.target; | ||
var isIntersecting = entry.isIntersecting; | ||
var boundingClientRect = entry.boundingClientRect; | ||
var target = entry.target; | ||
// bottom is how far bottom edge of el is from top of viewport | ||
var bottom = boundingClientRect.bottom; | ||
var height = boundingClientRect.height; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
// bottom = bottom edge of element from top of viewport | ||
// bottomAdjusted = bottom edge of element from trigger | ||
var top = boundingClientRect.top; | ||
var bottom = boundingClientRect.bottom; | ||
var topAdjusted = top - offsetMargin; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
if (bottomAdjusted >= -ZERO_MOE) { | ||
if (isIntersecting && direction === 'down' && ss.state !== 'enter') | ||
{ notifyStepEnter(target, direction); } | ||
else if (!isIntersecting && direction === 'up' && ss.state === 'enter') | ||
{ notifyStepExit(target, direction); } | ||
else if ( | ||
!isIntersecting && | ||
bottomAdjusted >= height && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) { | ||
notifyStepExit(target, direction); | ||
} | ||
} | ||
}); | ||
} | ||
// entering below is only when bottomAdjusted is positive | ||
// and topAdjusted is positive | ||
if ( | ||
isIntersecting && | ||
topAdjusted <= 0 && | ||
bottomAdjusted >= 0 && | ||
direction === 'up' && | ||
ss.state !== 'enter' | ||
) | ||
{ notifyStepEnter(target, direction); } | ||
function intersectStepBelow(entries) { | ||
updateDirection(); | ||
entries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting; | ||
var boundingClientRect = entry.boundingClientRect; | ||
var target = entry.target; | ||
var bottom = boundingClientRect.bottom; | ||
var height = boundingClientRect.height; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
if ( | ||
bottomAdjusted >= -ZERO_MOE && | ||
bottomAdjusted < height && | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, direction); | ||
} else if ( | ||
bottomAdjusted <= ZERO_MOE && | ||
!isIntersecting && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) { | ||
notifyStepExit(target, direction); | ||
} | ||
}); | ||
// exiting from above is when bottomAdjusted is negative and not intersecting | ||
if ( | ||
!isIntersecting && | ||
bottomAdjusted < 0 && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) | ||
{ notifyStepExit(target, direction); } | ||
} | ||
@@ -438,129 +423,66 @@ | ||
*/ | ||
function intersectViewportAbove(entries) { | ||
function intersectViewportAbove(ref) { | ||
var entry = ref[0]; | ||
updateDirection(); | ||
entries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting; | ||
var target = entry.target; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'down' && | ||
ss.state !== 'enter' && | ||
ss.direction !== 'down' | ||
) { | ||
notifyStepEnter(target, 'down'); | ||
notifyStepExit(target, 'down'); | ||
} | ||
}); | ||
} | ||
var isIntersecting = entry.isIntersecting; | ||
var target = entry.target; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
function intersectViewportBelow(entries) { | ||
updateDirection(); | ||
entries.forEach(function (entry) { | ||
var isIntersecting = entry.isIntersecting; | ||
var target = entry.target; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.state !== 'enter' && | ||
ss.direction !== 'up' | ||
) { | ||
notifyStepEnter(target, 'up'); | ||
notifyStepExit(target, 'up'); | ||
} | ||
}); | ||
if ( | ||
isIntersecting && | ||
direction === 'down' && | ||
ss.direction !== 'down' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, 'down'); | ||
notifyStepExit(target, 'down'); | ||
} | ||
} | ||
function intersectStepProgress(entries) { | ||
updateDirection(); | ||
entries.forEach( | ||
function (ref) { | ||
var isIntersecting = ref.isIntersecting; | ||
var intersectionRatio = ref.intersectionRatio; | ||
var boundingClientRect = ref.boundingClientRect; | ||
var target = ref.target; | ||
function intersectViewportBelow(ref) { | ||
var entry = ref[0]; | ||
var bottom = boundingClientRect.bottom; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
if (isIntersecting && bottomAdjusted >= -ZERO_MOE) { | ||
notifyStepProgress(target, +intersectionRatio.toFixed(3)); | ||
} | ||
} | ||
); | ||
} | ||
function intersectTop(entries) { | ||
updateDirection(); | ||
var ref = entries[0]; | ||
var isIntersecting = ref.isIntersecting; | ||
var boundingClientRect = ref.boundingClientRect; | ||
var top = boundingClientRect.top; | ||
var bottom = boundingClientRect.bottom; | ||
if (bottom > -ZERO_MOE) { | ||
if (isIntersecting) { notifyContainerEnter(direction); } | ||
else if (containerState.state === 'enter') { notifyContainerExit(direction); } | ||
var isIntersecting = entry.isIntersecting; | ||
var target = entry.target; | ||
var index = getIndex(target); | ||
var ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.direction === 'down' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, 'up'); | ||
notifyStepExit(target, 'up'); | ||
} | ||
} | ||
function intersectBottom(entries) { | ||
function intersectStepProgress(ref) { | ||
var entry = ref[0]; | ||
updateDirection(); | ||
var ref = entries[0]; | ||
var isIntersecting = ref.isIntersecting; | ||
var boundingClientRect = ref.boundingClientRect; | ||
var top = boundingClientRect.top; | ||
if (top < ZERO_MOE) { | ||
if (isIntersecting) { notifyContainerEnter(direction); } | ||
else if (containerState.state === 'enter') { notifyContainerExit(direction); } | ||
var isIntersecting = entry.isIntersecting; | ||
var intersectionRatio = entry.intersectionRatio; | ||
var boundingClientRect = entry.boundingClientRect; | ||
var target = entry.target; | ||
var bottom = boundingClientRect.bottom; | ||
var bottomAdjusted = bottom - offsetMargin; | ||
if (isIntersecting && bottomAdjusted >= 0) { | ||
notifyStepProgress(target, +intersectionRatio.toFixed(3)); | ||
} | ||
} | ||
// OBSERVER - CREATION | ||
function updateTopIO() { | ||
if (io.top) { io.top.unobserve(containerEl); } | ||
var options = { | ||
root: null, | ||
rootMargin: (vh + "px 0px -" + vh + "px 0px"), | ||
threshold: 0 | ||
}; | ||
io.top = new IntersectionObserver(intersectTop, options); | ||
io.top.observe(containerEl); | ||
} | ||
function updateBottomIO() { | ||
if (io.bottom) { io.bottom.unobserve(containerEl); } | ||
var options = { | ||
root: null, | ||
rootMargin: ("-" + (bboxGraphic.height) + "px 0px " + (bboxGraphic.height) + "px 0px"), | ||
threshold: 0 | ||
}; | ||
io.bottom = new IntersectionObserver(intersectBottom, options); | ||
io.bottom.observe(containerEl); | ||
} | ||
// top edge | ||
function updateStepAboveIO() { | ||
if (io.stepAbove) { io.stepAbove.forEach(function (d) { return d.disconnect(); }); } | ||
io.stepAbove = stepEl.map(function (el, i) { | ||
var marginTop = stepOffsetHeight[i]; | ||
var marginBottom = -vh + offsetMargin; | ||
/*** OBSERVER - CREATION ***/ | ||
// jump into viewport | ||
function updateViewportAboveIO() { | ||
io.viewportAbove = stepEl.map(function (el, i) { | ||
var marginTop = pageH - stepOffsetTop[i]; | ||
var marginBottom = offsetMargin - viewH - stepOffsetHeight[i]; | ||
var rootMargin = marginTop + "px 0px " + marginBottom + "px 0px"; | ||
var options = { | ||
root: null, | ||
rootMargin: rootMargin, | ||
threshold: 0 | ||
}; | ||
var obs = new IntersectionObserver(intersectStepAbove, options); | ||
var options = { rootMargin: rootMargin }; | ||
// console.log(options); | ||
var obs = new IntersectionObserver(intersectViewportAbove, options); | ||
obs.observe(el); | ||
@@ -571,18 +493,10 @@ return obs; | ||
// bottom edge | ||
function updateStepBelowIO() { | ||
if (io.stepBelow) { io.stepBelow.forEach(function (d) { return d.disconnect(); }); } | ||
io.stepBelow = stepEl.map(function (el, i) { | ||
var marginTop = -offsetMargin; | ||
var marginBottom = ph - vh + stepOffsetHeight[i] + offsetMargin; | ||
function updateViewportBelowIO() { | ||
io.viewportBelow = stepEl.map(function (el, i) { | ||
var marginTop = -offsetMargin - stepOffsetHeight[i]; | ||
var marginBottom = offsetMargin - viewH + stepOffsetHeight[i] + pageH; | ||
var rootMargin = marginTop + "px 0px " + marginBottom + "px 0px"; | ||
var options = { | ||
root: null, | ||
rootMargin: rootMargin, | ||
threshold: 0 | ||
}; | ||
var obs = new IntersectionObserver(intersectStepBelow, options); | ||
var options = { rootMargin: rootMargin }; | ||
// console.log(options); | ||
var obs = new IntersectionObserver(intersectViewportBelow, options); | ||
obs.observe(el); | ||
@@ -593,16 +507,11 @@ return obs; | ||
// jump into viewport | ||
function updateViewportAboveIO() { | ||
if (io.viewportAbove) { io.viewportAbove.forEach(function (d) { return d.disconnect(); }); } | ||
io.viewportAbove = stepEl.map(function (el, i) { | ||
var marginTop = stepOffsetTop[i]; | ||
var marginBottom = -(vh - offsetMargin + stepOffsetHeight[i]); | ||
// look above for intersection | ||
function updateStepAboveIO() { | ||
io.stepAbove = stepEl.map(function (el, i) { | ||
var marginTop = -offsetMargin + stepOffsetHeight[i]; | ||
var marginBottom = offsetMargin - viewH; | ||
var rootMargin = marginTop + "px 0px " + marginBottom + "px 0px"; | ||
var options = { | ||
root: null, | ||
rootMargin: rootMargin, | ||
threshold: 0 | ||
}; | ||
var obs = new IntersectionObserver(intersectViewportAbove, options); | ||
var options = { rootMargin: rootMargin }; | ||
// console.log(options); | ||
var obs = new IntersectionObserver(intersectStepAbove, options); | ||
obs.observe(el); | ||
@@ -613,16 +522,11 @@ return obs; | ||
function updateViewportBelowIO() { | ||
if (io.viewportBelow) { io.viewportBelow.forEach(function (d) { return d.disconnect(); }); } | ||
io.viewportBelow = stepEl.map(function (el, i) { | ||
var marginTop = -(offsetMargin + stepOffsetHeight[i]); | ||
var marginBottom = | ||
ph - stepOffsetTop[i] - stepOffsetHeight[i] - offsetMargin; | ||
// look below for intersection | ||
function updateStepBelowIO() { | ||
io.stepAbove = stepEl.map(function (el, i) { | ||
var marginTop = -offsetMargin; | ||
var marginBottom = offsetMargin - viewH + stepOffsetHeight[i]; | ||
var rootMargin = marginTop + "px 0px " + marginBottom + "px 0px"; | ||
var options = { | ||
root: null, | ||
rootMargin: rootMargin, | ||
threshold: 0 | ||
}; | ||
var obs = new IntersectionObserver(intersectViewportBelow, options); | ||
var options = { rootMargin: rootMargin }; | ||
// console.log(options); | ||
var obs = new IntersectionObserver(intersectStepBelow, options); | ||
obs.observe(el); | ||
@@ -635,16 +539,9 @@ return obs; | ||
function updateStepProgressIO() { | ||
if (io.stepProgress) { io.stepProgress.forEach(function (d) { return d.disconnect(); }); } | ||
io.stepProgress = stepEl.map(function (el, i) { | ||
var marginTop = stepOffsetHeight[i] - offsetMargin; | ||
var marginBottom = -vh + offsetMargin; | ||
var marginBottom = -viewH + offsetMargin; | ||
var rootMargin = marginTop + "px 0px " + marginBottom + "px 0px"; | ||
var threshold = createThreshold(stepOffsetHeight[i]); | ||
var options = { | ||
root: null, | ||
rootMargin: rootMargin, | ||
threshold: threshold | ||
}; | ||
var options = { rootMargin: rootMargin, threshold: threshold }; | ||
// console.log(options); | ||
var obs = new IntersectionObserver(intersectStepProgress, options); | ||
@@ -657,2 +554,4 @@ obs.observe(el); | ||
function updateIO() { | ||
OBSERVER_NAMES.forEach(disconnectObserver); | ||
updateViewportAboveIO(); | ||
@@ -664,10 +563,5 @@ updateViewportBelowIO(); | ||
if (progressMode) { updateStepProgressIO(); } | ||
if (containerEl && graphicEl) { | ||
updateTopIO(); | ||
updateBottomIO(); | ||
} | ||
} | ||
// SETUP FUNCTIONS | ||
/*** SETUP FUNCTIONS ***/ | ||
@@ -681,10 +575,9 @@ function indexSteps() { | ||
direction: null, | ||
state: null | ||
state: null, | ||
progress: 0 | ||
}); }); | ||
containerState = { direction: null, state: null }; | ||
} | ||
function addDebug() { | ||
if (debugMode) { setup({ id: id, stepEl: stepEl, offsetVal: offsetVal }); } | ||
if (isDebug) { setup({ id: id, stepEl: stepEl, offsetVal: offsetVal }); } | ||
} | ||
@@ -695,4 +588,2 @@ | ||
S.setup = function (ref) { | ||
var container = ref.container; | ||
var graphic = ref.graphic; | ||
var step = ref.step; | ||
@@ -706,9 +597,7 @@ var offset = ref.offset; if ( offset === void 0 ) offset = 0.5; | ||
id = generateId(); | ||
// elements | ||
// create id unique to this scrollama instance | ||
id = generateInstanceID(); | ||
stepEl = selectAll(step); | ||
containerEl = container ? select(container) : null; | ||
graphicEl = graphic ? select(graphic) : null; | ||
// error if no step selected | ||
if (!stepEl.length) { | ||
@@ -720,3 +609,3 @@ console.error('scrollama error: no step elements'); | ||
// options | ||
debugMode = debug; | ||
isDebug = debug; | ||
progressMode = progress; | ||
@@ -736,3 +625,3 @@ preserveOrder = order; | ||
handleResize(); | ||
handleEnable(true); | ||
S.enable(); | ||
return S; | ||
@@ -758,3 +647,3 @@ }; | ||
handleEnable(false); | ||
Object.keys(callback).forEach(function (c) { return (callback[c] = null); }); | ||
Object.keys(cb).forEach(function (c) { return (cb[c] = null); }); | ||
Object.keys(io).forEach(function (i) { return (io[i] = null); }); | ||
@@ -771,27 +660,20 @@ }; | ||
S.onStepEnter = function (cb) { | ||
callback.stepEnter = cb; | ||
S.onStepEnter = function (f) { | ||
if (typeof f === 'function') { cb.stepEnter = f; } | ||
else { console.error('scrollama error: onStepEnter requires a function'); } | ||
return S; | ||
}; | ||
S.onStepExit = function (cb) { | ||
callback.stepExit = cb; | ||
S.onStepExit = function (f) { | ||
if (typeof f === 'function') { cb.stepExit = f; } | ||
else { console.error('scrollama error: onStepExit requires a function'); } | ||
return S; | ||
}; | ||
S.onStepProgress = function (cb) { | ||
callback.stepProgress = cb; | ||
S.onStepProgress = function (f) { | ||
if (typeof f === 'function') { cb.stepProgress = f; } | ||
else { console.error('scrollama error: onStepProgress requires a function'); } | ||
return S; | ||
}; | ||
S.onContainerEnter = function (cb) { | ||
callback.containerEnter = cb; | ||
return S; | ||
}; | ||
S.onContainerExit = function (cb) { | ||
callback.containerExit = cb; | ||
return S; | ||
}; | ||
return S; | ||
@@ -798,0 +680,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.scrollama=e()}(this,function(){"use strict";function t(t){for(var e=t.length,n=[],o=0;o<e;o+=1)n.push(t[o]);return n}function e(t){return t instanceof Element?t:"string"==typeof t?document.querySelector(t):null}function n(t){return"scrollama__debug-offset--"+t.id}function o(t){!function(t){var e=t.id,o=t.offsetVal,r=t.stepClass,i=document.createElement("div");i.setAttribute("id",n({id:e})),i.setAttribute("class","scrollama__debug-offset"),i.style.position="fixed",i.style.left="0",i.style.width="100%",i.style.height="0px",i.style.borderTop="2px dashed black",i.style.zIndex="9999";var s=document.createElement("p");s.innerText='".'+r+'" trigger: '+o,s.style.fontSize="12px",s.style.fontFamily="monospace",s.style.color="black",s.style.margin="0",s.style.padding="6px",i.appendChild(s),document.body.appendChild(i)}({id:t.id,offsetVal:t.offsetVal,stepClass:t.stepEl[0].getAttribute("class")})}function r(t){var e=t.id,o=(t.stepOffsetHeight,t.offsetMargin);t.offsetVal;!function(t){var e=t.id,o=t.offsetMargin,r=(t.offsetVal,n({id:e}));document.querySelector("#"+r).style.top=o+"px"}({id:e,offsetMargin:o})}function i(t){var e=t.id,n=t.index,o=t.state,r=function(t){return"scrollama__debug-step--"+t.id+"-"+t.i}({id:e,i:n}),i=document.querySelector("#"+r+"_above"),s=document.querySelector("#"+r+"_below"),c="enter"===o?"block":"none";i&&(i.style.display=c),s&&(s.style.display=c)}return function(){var n=1,s={},c={},u=null,a=null,f=null,l=null,p=0,d=0,v=0,g=0,h=null,b=null,x=null,m=!1,w=!1,E=!1,y=!1,M=0,A=!1,I=!1,O=null,C=null,P=-1,B=null,H=[];function S(t){var e=0;if(t.offsetParent)do{e+=t.offsetTop,t=t.offsetParent}while(t);return e<0?0:e}function _(t){return+t.getAttribute("data-scrollama-index")}function R(){window.pageYOffset>P?B="down":window.pageYOffset<P&&(B="up"),P=window.pageYOffset}function V(){var t,e;v=window.innerHeight,t=document.body,e=document.documentElement,g=Math.max(t.scrollHeight,t.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight),x=a?a.getBoundingClientRect():null,d=p*v,h=f?f.map(function(t){return t.offsetHeight}):[],b=f?f.map(S):[],w&&m&&W(),E&&r({id:l,stepOffsetHeight:h,offsetMargin:d,offsetVal:p})}function k(t){t&&!w?(m&&W(),w=!0):t||(c.top&&c.top.disconnect(),c.bottom&&c.bottom.disconnect(),c.stepAbove&&c.stepAbove.forEach(function(t){return t.disconnect()}),c.stepBelow&&c.stepBelow.forEach(function(t){return t.disconnect()}),c.stepProgress&&c.stepProgress.forEach(function(t){return t.disconnect()}),c.viewportAbove&&c.viewportAbove.forEach(function(t){return t.disconnect()}),c.viewportBelow&&c.viewportBelow.forEach(function(t){return t.disconnect()}),w=!1)}function q(t,e){if("above"===e)for(var n=0;n<t;n++){var o=O[n];"enter"===o.state&&j(f[n],"down"),"up"===o.direction&&(T(f[n],"down",!1),j(f[n],"down"))}else if("below"===e)for(var r=O.length-1;r>t;r--){var i=O[r];"enter"===i.state&&j(f[r],"up"),"down"===i.direction&&(T(f[r],"up",!1),j(f[r],"up"))}}function T(t,e,n){void 0===n&&(n=!0);var o=_(t),r={element:t,index:o,direction:e};O[o].direction=e,O[o].state="enter",A&&n&&"down"===e&&q(o,"above"),A&&n&&"up"===e&&q(o,"below"),s.stepEnter&&"function"==typeof s.stepEnter&&!H[o]&&(s.stepEnter(r,O),E&&i({id:l,index:o,state:"enter"}),I&&(H[o]=!0)),y&&z(t,"down"===e?0:1)}function j(t,e){var n=_(t),o={element:t,index:n,direction:e};O[n].direction=e,O[n].state="exit",y&&z(t,"down"===e?1:0),s.stepExit&&"function"==typeof s.stepExit&&(s.stepExit(o,O),E&&i({id:l,index:n,state:"exit"}))}function z(t,e){var n={element:t,index:_(t),progress:e};s.stepProgress&&"function"==typeof s.stepProgress&&s.stepProgress(n)}function N(){var t={direction:B};C.direction=B,C.state="enter",s.containerEnter&&"function"==typeof s.containerEnter&&s.containerEnter(t)}function Y(){var t={direction:B};C.direction=B,C.state="exit",s.containerExit&&"function"==typeof s.containerExit&&s.containerExit(t)}function F(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.boundingClientRect,r=t.target,i=o.bottom,s=o.height,c=i-d,u=_(r),a=O[u];c>=-n&&(e&&"down"===B&&"enter"!==a.state?T(r,B):e||"up"!==B||"enter"!==a.state?!e&&c>=s&&"down"===B&&"enter"===a.state&&j(r,B):j(r,B))})}function D(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.boundingClientRect,r=t.target,i=o.bottom,s=o.height,c=i-d,u=_(r),a=O[u];c>=-n&&c<s&&e&&"up"===B&&"enter"!==a.state?T(r,B):c<=n&&!e&&"down"===B&&"enter"===a.state&&j(r,B)})}function L(t){R(),t.forEach(function(t){var e=t.isIntersecting,n=t.target,o=_(n),r=O[o];e&&"down"===B&&"enter"!==r.state&&"down"!==r.direction&&(T(n,"down"),j(n,"down"))})}function G(t){R(),t.forEach(function(t){var e=t.isIntersecting,n=t.target,o=_(n),r=O[o];e&&"up"===B&&"enter"!==r.state&&"up"!==r.direction&&(T(n,"up"),j(n,"up"))})}function J(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.intersectionRatio,r=t.boundingClientRect,i=t.target,s=r.bottom;e&&s-d>=-n&&z(i,+o.toFixed(3))})}function K(t){R();var e=t[0],o=e.isIntersecting,r=e.boundingClientRect;r.top,r.bottom>-n&&(o?N():"enter"===C.state&&Y())}function Q(t){R();var e=t[0],o=e.isIntersecting;e.boundingClientRect.top<n&&(o?N():"enter"===C.state&&Y())}function U(){c.stepProgress&&c.stepProgress.forEach(function(t){return t.disconnect()}),c.stepProgress=f.map(function(t,e){var n=h[e]-d+"px 0px "+(-v+d)+"px 0px",o=function(t){for(var e=Math.ceil(t/M),n=[],o=1/e,r=0;r<e;r++)n.push(r*o);return n}(h[e]),r=new IntersectionObserver(J,{root:null,rootMargin:n,threshold:o});return r.observe(t),r})}function W(){c.viewportAbove&&c.viewportAbove.forEach(function(t){return t.disconnect()}),c.viewportAbove=f.map(function(t,e){var n=b[e],o=-(v-d+h[e]),r=new IntersectionObserver(L,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),c.viewportBelow&&c.viewportBelow.forEach(function(t){return t.disconnect()}),c.viewportBelow=f.map(function(t,e){var n=-(d+h[e]),o=g-b[e]-h[e]-d,r=new IntersectionObserver(G,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),c.stepAbove&&c.stepAbove.forEach(function(t){return t.disconnect()}),c.stepAbove=f.map(function(t,e){var n=h[e],o=new IntersectionObserver(F,{root:null,rootMargin:n+"px 0px "+(-v+d)+"px 0px",threshold:0});return o.observe(t),o}),c.stepBelow&&c.stepBelow.forEach(function(t){return t.disconnect()}),c.stepBelow=f.map(function(t,e){var n=-d,o=g-v+h[e]+d,r=new IntersectionObserver(D,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),y&&U(),u&&a&&(function(){c.top&&c.top.unobserve(u);var t={root:null,rootMargin:v+"px 0px -"+v+"px 0px",threshold:0};c.top=new IntersectionObserver(K,t),c.top.observe(u)}(),function(){c.bottom&&c.bottom.unobserve(u);var t={root:null,rootMargin:"-"+x.height+"px 0px "+x.height+"px 0px",threshold:0};c.bottom=new IntersectionObserver(Q,t),c.bottom.observe(u)}())}var X={};return X.setup=function(n){var r=n.container,i=n.graphic,s=n.step,c=n.offset;void 0===c&&(c=.5);var d=n.progress;void 0===d&&(d=!1);var v=n.threshold;void 0===v&&(v=4);var g=n.debug;void 0===g&&(g=!1);var h=n.order;void 0===h&&(h=!0);var b,x,w,P,B,H=n.once;return void 0===H&&(H=!1),x=(b="abcdefghijklmnopqrstuv").length,w=(new Date).getTime(),l=""+[0,0,0].map(function(t){return b[Math.floor(Math.random()*x)]}).join("")+w,P=s,void 0===B&&(B=document),f="string"==typeof P?t(B.querySelectorAll(P)):P instanceof Element?t([P]):P instanceof NodeList?t(P):P instanceof Array?P:[],u=r?e(r):null,a=i?e(i):null,f.length?(E=g,y=d,A=h,I=H,X.offsetTrigger(c),M=Math.max(1,+v),m=!0,E&&o({id:l,stepEl:f,offsetVal:p}),f.forEach(function(t,e){return t.setAttribute("data-scrollama-index",e)}),O=f.map(function(){return{direction:null,state:null}}),C={direction:null,state:null},V(),k(!0),X):(console.error("scrollama error: no step elements"),X)},X.resize=function(){return V(),X},X.enable=function(){return k(!0),X},X.disable=function(){return k(!1),X},X.destroy=function(){k(!1),Object.keys(s).forEach(function(t){return s[t]=null}),Object.keys(c).forEach(function(t){return c[t]=null})},X.offsetTrigger=function(t){return t&&!isNaN(t)?(p=Math.min(Math.max(0,t),1),X):p},X.onStepEnter=function(t){return s.stepEnter=t,X},X.onStepExit=function(t){return s.stepExit=t,X},X.onStepProgress=function(t){return s.stepProgress=t,X},X.onContainerEnter=function(t){return s.containerEnter=t,X},X.onContainerExit=function(t){return s.containerExit=t,X},X}}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.scrollama=t()}(this,function(){"use strict";function e(e){for(var t=e.length,n=[],r=0;r<t;r+=1)n.push(e[r]);return n}function t(e){return"scrollama__debug-offset--"+e.id}function n(e){!function(e){var n=e.id,r=e.offsetVal,o=e.stepClass,i=document.createElement("div");i.setAttribute("id",t({id:n})),i.setAttribute("class","scrollama__debug-offset"),i.style.position="fixed",i.style.left="0",i.style.width="100%",i.style.height="0px",i.style.borderTop="2px dashed black",i.style.zIndex="9999";var s=document.createElement("p");s.innerText='".'+o+'" trigger: '+r,s.style.fontSize="12px",s.style.fontFamily="monospace",s.style.color="black",s.style.margin="0",s.style.padding="6px",i.appendChild(s),document.body.appendChild(i)}({id:e.id,offsetVal:e.offsetVal,stepClass:e.stepEl[0].getAttribute("class")})}function r(e){var n=e.id,r=(e.stepOffsetHeight,e.offsetMargin);e.offsetVal;!function(e){var n=e.id,r=e.offsetMargin,o=(e.offsetVal,t({id:n}));document.querySelector("#"+o).style.top=r+"px"}({id:n,offsetMargin:r})}function o(e){var t=e.id,n=e.index,r=e.state,o=function(e){return"scrollama__debug-step--"+e.id+"-"+e.i}({id:t,i:n}),i=document.querySelector("#"+o+"_above"),s=document.querySelector("#"+o+"_below"),a="enter"===r?"block":"none";i&&(i.style.display=a),s&&(s.style.display=a)}return function(){var t=["stepAbove","stepBelow","stepProgress","viewportAbove","viewportBelow"],i={stepEnter:function(){},stepExit:function(){},stepProgress:function(){}},s={},a=null,f=[],u=[],c=[],d=[],p=0,l=0,v=0,g=0,m=0,b=0,x=!1,w=!1,h=!1,y=!1,E=!1,M=!1,A="down",O=[];function I(e){var t=0;if(e.offsetParent)do{t+=e.offsetTop,e=e.offsetParent}while(e);return t<0?0:t}function S(e){return+e.getAttribute("data-scrollama-index")}function H(){window.pageYOffset>m?A="down":window.pageYOffset<m&&(A="up"),m=window.pageYOffset}function P(e){s[e]&&s[e].forEach(function(e){return e.disconnect()})}function q(){var e,t;v=window.innerHeight,e=document.body,t=document.documentElement,g=Math.max(e.scrollHeight,e.offsetHeight,t.clientHeight,t.scrollHeight,t.offsetHeight),l=p*v,x&&(u=f.map(function(e){return e.offsetHeight}),c=f.map(I),w&&F()),h&&r({id:a,stepOffsetHeight:u,offsetMargin:l,offsetVal:p})}function _(e){if(e&&!w)return x&&F(),w=!0,!0;t.forEach(P),w=!1}function C(e,t){var n=S(e);void 0!==t&&(d[n].progress=t);var r={element:e,index:n,progress:d[n].progress};"enter"===d[n].state&&i.stepProgress(r)}function V(e,t){if("above"===t)for(var n=0;n<e;n++){var r=d[n];"enter"!==r.state&&"down"!==r.direction?(k(f[n],"down",!1),j(f[n],"down")):"enter"===r.state&&j(f[n],"down")}else if("below"===t)for(var o=d.length-1;o>e;o--){var i=d[o];"enter"===i.state&&j(f[o],"up"),"down"===i.direction&&(k(f[o],"up",!1),j(f[o],"up"))}}function k(e,t,n){void 0===n&&(n=!0);var r=S(e),s={element:e,index:r,direction:t};d[r].direction=t,d[r].state="enter",E&&n&&"down"===t&&V(r,"above"),E&&n&&"up"===t&&V(r,"below"),i.stepEnter&&!O[r]&&(i.stepEnter(s,d),h&&o({id:a,index:r,state:"enter"}),M&&(O[r]=!0)),y&&C(e)}function j(e,t){var n=S(e),r={element:e,index:n,direction:t};y&&("down"===t&&d[n].progress<1?C(e,1):"up"===t&&d[n].progress>0&&C(e,0)),d[n].direction=t,d[n].state="exit",i.stepExit(r,d),h&&o({id:a,index:n,state:"exit"})}function T(e){var t=e[0];H();var n=t.isIntersecting,r=t.boundingClientRect,o=t.target,i=r.top,s=r.bottom,a=i-l,f=s-l,u=S(o),c=d[u];n&&a<=0&&f>=0&&"down"===A&&"enter"!==c.state&&k(o,A),!n&&a>0&&"up"===A&&"enter"===c.state&&j(o,A)}function R(e){var t=e[0];H();var n=t.isIntersecting,r=t.boundingClientRect,o=t.target,i=r.top,s=r.bottom,a=i-l,f=s-l,u=S(o),c=d[u];n&&a<=0&&f>=0&&"up"===A&&"enter"!==c.state&&k(o,A),!n&&f<0&&"down"===A&&"enter"===c.state&&j(o,A)}function z(e){var t=e[0];H();var n=t.isIntersecting,r=t.target,o=S(r),i=d[o];n&&"down"===A&&"down"!==i.direction&&"enter"!==i.state&&(k(r,"down"),j(r,"down"))}function B(e){var t=e[0];H();var n=t.isIntersecting,r=t.target,o=S(r),i=d[o];n&&"up"===A&&"down"===i.direction&&"enter"!==i.state&&(k(r,"up"),j(r,"up"))}function N(e){var t=e[0];H();var n=t.isIntersecting,r=t.intersectionRatio,o=t.boundingClientRect,i=t.target,s=o.bottom;n&&s-l>=0&&C(i,+r.toFixed(3))}function Y(){s.stepProgress=f.map(function(e,t){var n=u[t]-l+"px 0px "+(-v+l)+"px 0px",r=function(e){for(var t=Math.ceil(e/b),n=[],r=1/t,o=0;o<t;o++)n.push(o*r);return n}(u[t]),o=new IntersectionObserver(N,{rootMargin:n,threshold:r});return o.observe(e),o})}function F(){t.forEach(P),s.viewportAbove=f.map(function(e,t){var n=g-c[t],r=l-v-u[t],o=new IntersectionObserver(z,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),s.viewportBelow=f.map(function(e,t){var n=-l-u[t],r=l-v+u[t]+g,o=new IntersectionObserver(B,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),s.stepAbove=f.map(function(e,t){var n=-l+u[t],r=new IntersectionObserver(T,{rootMargin:n+"px 0px "+(l-v)+"px 0px"});return r.observe(e),r}),s.stepAbove=f.map(function(e,t){var n=-l,r=l-v+u[t],o=new IntersectionObserver(R,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),y&&Y()}var D={};return D.setup=function(t){var r=t.step,o=t.offset;void 0===o&&(o=.5);var i=t.progress;void 0===i&&(i=!1);var s=t.threshold;void 0===s&&(s=4);var u=t.debug;void 0===u&&(u=!1);var c=t.order;void 0===c&&(c=!0);var l,v,g,m,w,A=t.once;return void 0===A&&(A=!1),v=(l="abcdefghijklmnopqrstuv").length,g=Date.now(),a=""+[0,0,0].map(function(e){return l[Math.floor(Math.random()*v)]}).join("")+g,m=r,void 0===w&&(w=document),(f="string"==typeof m?e(w.querySelectorAll(m)):m instanceof Element?e([m]):m instanceof NodeList?e(m):m instanceof Array?m:[]).length?(h=u,y=i,E=c,M=A,D.offsetTrigger(o),b=Math.max(1,+s),x=!0,h&&n({id:a,stepEl:f,offsetVal:p}),f.forEach(function(e,t){return e.setAttribute("data-scrollama-index",t)}),d=f.map(function(){return{direction:null,state:null,progress:0}}),q(),D.enable(),D):(console.error("scrollama error: no step elements"),D)},D.resize=function(){return q(),D},D.enable=function(){return _(!0),D},D.disable=function(){return _(!1),D},D.destroy=function(){_(!1),Object.keys(i).forEach(function(e){return i[e]=null}),Object.keys(s).forEach(function(e){return s[e]=null})},D.offsetTrigger=function(e){return e&&!isNaN(e)?(p=Math.min(Math.max(0,e),1),D):p},D.onStepEnter=function(e){return"function"==typeof e?i.stepEnter=e:console.error("scrollama error: onStepEnter requires a function"),D},D.onStepExit=function(e){return"function"==typeof e?i.stepExit=e:console.error("scrollama error: onStepExit requires a function"),D},D.onStepProgress=function(e){return"function"==typeof e?i.stepProgress=e:console.error("scrollama error: onStepProgress requires a function"),D},D}}); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.scrollama=e()}(this,function(){"use strict";function t(t){for(var e=t.length,n=[],o=0;o<e;o+=1)n.push(t[o]);return n}function e(t){return t instanceof Element?t:"string"==typeof t?document.querySelector(t):null}function n(t){return"scrollama__debug-offset--"+t.id}function o(t){!function(t){var e=t.id,o=t.offsetVal,r=t.stepClass,i=document.createElement("div");i.setAttribute("id",n({id:e})),i.setAttribute("class","scrollama__debug-offset"),i.style.position="fixed",i.style.left="0",i.style.width="100%",i.style.height="0px",i.style.borderTop="2px dashed black",i.style.zIndex="9999";var s=document.createElement("p");s.innerText='".'+r+'" trigger: '+o,s.style.fontSize="12px",s.style.fontFamily="monospace",s.style.color="black",s.style.margin="0",s.style.padding="6px",i.appendChild(s),document.body.appendChild(i)}({id:t.id,offsetVal:t.offsetVal,stepClass:t.stepEl[0].getAttribute("class")})}function r(t){var e=t.id,o=(t.stepOffsetHeight,t.offsetMargin);t.offsetVal;!function(t){var e=t.id,o=t.offsetMargin,r=(t.offsetVal,n({id:e}));document.querySelector("#"+r).style.top=o+"px"}({id:e,offsetMargin:o})}function i(t){var e=t.id,n=t.index,o=t.state,r=function(t){return"scrollama__debug-step--"+t.id+"-"+t.i}({id:e,i:n}),i=document.querySelector("#"+r+"_above"),s=document.querySelector("#"+r+"_below"),c="enter"===o?"block":"none";i&&(i.style.display=c),s&&(s.style.display=c)}return function(){var n=1,s={},c={},u=null,a=null,f=null,l=null,p=0,d=0,v=0,g=0,h=null,b=null,x=null,m=!1,w=!1,E=!1,y=!1,M=0,A=!1,I=!1,O=null,C=null,P=-1,B=null,H=[];function S(t){var e=0;if(t.offsetParent)do{e+=t.offsetTop,t=t.offsetParent}while(t);return e<0?0:e}function _(t){return+t.getAttribute("data-scrollama-index")}function R(){window.pageYOffset>P?B="down":window.pageYOffset<P&&(B="up"),P=window.pageYOffset}function V(){var t,e;v=window.innerHeight,t=document.body,e=document.documentElement,g=Math.max(t.scrollHeight,t.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight),x=a?a.getBoundingClientRect():null,d=p*v,h=f?f.map(function(t){return t.offsetHeight}):[],b=f?f.map(S):[],w&&m&&W(),E&&r({id:l,stepOffsetHeight:h,offsetMargin:d,offsetVal:p})}function k(t){t&&!w?(m&&W(),w=!0):t||(c.top&&c.top.disconnect(),c.bottom&&c.bottom.disconnect(),c.stepAbove&&c.stepAbove.forEach(function(t){return t.disconnect()}),c.stepBelow&&c.stepBelow.forEach(function(t){return t.disconnect()}),c.stepProgress&&c.stepProgress.forEach(function(t){return t.disconnect()}),c.viewportAbove&&c.viewportAbove.forEach(function(t){return t.disconnect()}),c.viewportBelow&&c.viewportBelow.forEach(function(t){return t.disconnect()}),w=!1)}function q(t,e){if("above"===e)for(var n=0;n<t;n++){var o=O[n];"enter"===o.state&&j(f[n],"down"),"up"===o.direction&&(T(f[n],"down",!1),j(f[n],"down"))}else if("below"===e)for(var r=O.length-1;r>t;r--){var i=O[r];"enter"===i.state&&j(f[r],"up"),"down"===i.direction&&(T(f[r],"up",!1),j(f[r],"up"))}}function T(t,e,n){void 0===n&&(n=!0);var o=_(t),r={element:t,index:o,direction:e};O[o].direction=e,O[o].state="enter",A&&n&&"down"===e&&q(o,"above"),A&&n&&"up"===e&&q(o,"below"),s.stepEnter&&"function"==typeof s.stepEnter&&!H[o]&&(s.stepEnter(r,O),E&&i({id:l,index:o,state:"enter"}),I&&(H[o]=!0)),y&&z(t,"down"===e?0:1)}function j(t,e){var n=_(t),o={element:t,index:n,direction:e};O[n].direction=e,O[n].state="exit",y&&z(t,"down"===e?1:0),s.stepExit&&"function"==typeof s.stepExit&&(s.stepExit(o,O),E&&i({id:l,index:n,state:"exit"}))}function z(t,e){var n={element:t,index:_(t),progress:e};s.stepProgress&&"function"==typeof s.stepProgress&&s.stepProgress(n)}function N(){var t={direction:B};C.direction=B,C.state="enter",s.containerEnter&&"function"==typeof s.containerEnter&&s.containerEnter(t)}function Y(){var t={direction:B};C.direction=B,C.state="exit",s.containerExit&&"function"==typeof s.containerExit&&s.containerExit(t)}function F(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.boundingClientRect,r=t.target,i=o.bottom,s=o.height,c=i-d,u=_(r),a=O[u];c>=-n&&(e&&"down"===B&&"enter"!==a.state?T(r,B):e||"up"!==B||"enter"!==a.state?!e&&c>=s&&"down"===B&&"enter"===a.state&&j(r,B):j(r,B))})}function D(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.boundingClientRect,r=t.target,i=o.bottom,s=o.height,c=i-d,u=_(r),a=O[u];c>=-n&&c<s&&e&&"up"===B&&"enter"!==a.state?T(r,B):c<=n&&!e&&"down"===B&&"enter"===a.state&&j(r,B)})}function L(t){R(),t.forEach(function(t){var e=t.isIntersecting,n=t.target,o=_(n),r=O[o];e&&"down"===B&&"enter"!==r.state&&"down"!==r.direction&&(T(n,"down"),j(n,"down"))})}function G(t){R(),t.forEach(function(t){var e=t.isIntersecting,n=t.target,o=_(n),r=O[o];e&&"up"===B&&"enter"!==r.state&&"up"!==r.direction&&(T(n,"up"),j(n,"up"))})}function J(t){R(),t.forEach(function(t){var e=t.isIntersecting,o=t.intersectionRatio,r=t.boundingClientRect,i=t.target,s=r.bottom;e&&s-d>=-n&&z(i,+o.toFixed(3))})}function K(t){R();var e=t[0],o=e.isIntersecting,r=e.boundingClientRect;r.top,r.bottom>-n&&(o?N():"enter"===C.state&&Y())}function Q(t){R();var e=t[0],o=e.isIntersecting;e.boundingClientRect.top<n&&(o?N():"enter"===C.state&&Y())}function U(){c.stepProgress&&c.stepProgress.forEach(function(t){return t.disconnect()}),c.stepProgress=f.map(function(t,e){var n=h[e]-d+"px 0px "+(-v+d)+"px 0px",o=function(t){for(var e=Math.ceil(t/M),n=[],o=1/e,r=0;r<e;r++)n.push(r*o);return n}(h[e]),r=new IntersectionObserver(J,{root:null,rootMargin:n,threshold:o});return r.observe(t),r})}function W(){c.viewportAbove&&c.viewportAbove.forEach(function(t){return t.disconnect()}),c.viewportAbove=f.map(function(t,e){var n=b[e],o=-(v-d+h[e]),r=new IntersectionObserver(L,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),c.viewportBelow&&c.viewportBelow.forEach(function(t){return t.disconnect()}),c.viewportBelow=f.map(function(t,e){var n=-(d+h[e]),o=g-b[e]-h[e]-d,r=new IntersectionObserver(G,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),c.stepAbove&&c.stepAbove.forEach(function(t){return t.disconnect()}),c.stepAbove=f.map(function(t,e){var n=h[e],o=new IntersectionObserver(F,{root:null,rootMargin:n+"px 0px "+(-v+d)+"px 0px",threshold:0});return o.observe(t),o}),c.stepBelow&&c.stepBelow.forEach(function(t){return t.disconnect()}),c.stepBelow=f.map(function(t,e){var n=-d,o=g-v+h[e]+d,r=new IntersectionObserver(D,{root:null,rootMargin:n+"px 0px "+o+"px 0px",threshold:0});return r.observe(t),r}),y&&U(),u&&a&&(function(){c.top&&c.top.unobserve(u);var t={root:null,rootMargin:v+"px 0px -"+v+"px 0px",threshold:0};c.top=new IntersectionObserver(K,t),c.top.observe(u)}(),function(){c.bottom&&c.bottom.unobserve(u);var t={root:null,rootMargin:"-"+x.height+"px 0px "+x.height+"px 0px",threshold:0};c.bottom=new IntersectionObserver(Q,t),c.bottom.observe(u)}())}var X={};return X.setup=function(n){var r=n.container,i=n.graphic,s=n.step,c=n.offset;void 0===c&&(c=.5);var d=n.progress;void 0===d&&(d=!1);var v=n.threshold;void 0===v&&(v=4);var g=n.debug;void 0===g&&(g=!1);var h=n.order;void 0===h&&(h=!0);var b,x,w,P,B,H=n.once;return void 0===H&&(H=!1),x=(b="abcdefghijklmnopqrstuv").length,w=(new Date).getTime(),l=""+[0,0,0].map(function(t){return b[Math.floor(Math.random()*x)]}).join("")+w,P=s,void 0===B&&(B=document),f="string"==typeof P?t(B.querySelectorAll(P)):P instanceof Element?t([P]):P instanceof NodeList?t(P):P instanceof Array?P:[],u=r?e(r):null,a=i?e(i):null,f.length?(E=g,y=d,A=h,I=H,X.offsetTrigger(c),M=Math.max(1,+v),m=!0,E&&o({id:l,stepEl:f,offsetVal:p}),f.forEach(function(t,e){return t.setAttribute("data-scrollama-index",e)}),O=f.map(function(){return{direction:null,state:null}}),C={direction:null,state:null},V(),k(!0),X):(console.error("scrollama error: no step elements"),X)},X.resize=function(){return V(),X},X.enable=function(){return k(!0),X},X.disable=function(){return k(!1),X},X.destroy=function(){k(!1),Object.keys(s).forEach(function(t){return s[t]=null}),Object.keys(c).forEach(function(t){return c[t]=null})},X.offsetTrigger=function(t){return t&&!isNaN(t)?(p=Math.min(Math.max(0,t),1),X):p},X.onStepEnter=function(t){return s.stepEnter=t,X},X.onStepExit=function(t){return s.stepExit=t,X},X.onStepProgress=function(t){return s.stepProgress=t,X},X.onContainerEnter=function(t){return s.containerEnter=t,X},X.onContainerExit=function(t){return s.containerExit=t,X},X}}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.scrollama=t()}(this,function(){"use strict";function e(e){for(var t=e.length,n=[],r=0;r<t;r+=1)n.push(e[r]);return n}function t(e){return"scrollama__debug-offset--"+e.id}function n(e){!function(e){var n=e.id,r=e.offsetVal,o=e.stepClass,i=document.createElement("div");i.setAttribute("id",t({id:n})),i.setAttribute("class","scrollama__debug-offset"),i.style.position="fixed",i.style.left="0",i.style.width="100%",i.style.height="0px",i.style.borderTop="2px dashed black",i.style.zIndex="9999";var s=document.createElement("p");s.innerText='".'+o+'" trigger: '+r,s.style.fontSize="12px",s.style.fontFamily="monospace",s.style.color="black",s.style.margin="0",s.style.padding="6px",i.appendChild(s),document.body.appendChild(i)}({id:e.id,offsetVal:e.offsetVal,stepClass:e.stepEl[0].getAttribute("class")})}function r(e){var n=e.id,r=(e.stepOffsetHeight,e.offsetMargin);e.offsetVal;!function(e){var n=e.id,r=e.offsetMargin,o=(e.offsetVal,t({id:n}));document.querySelector("#"+o).style.top=r+"px"}({id:n,offsetMargin:r})}function o(e){var t=e.id,n=e.index,r=e.state,o=function(e){return"scrollama__debug-step--"+e.id+"-"+e.i}({id:t,i:n}),i=document.querySelector("#"+o+"_above"),s=document.querySelector("#"+o+"_below"),a="enter"===r?"block":"none";i&&(i.style.display=a),s&&(s.style.display=a)}return function(){var t=["stepAbove","stepBelow","stepProgress","viewportAbove","viewportBelow"],i={stepEnter:function(){},stepExit:function(){},stepProgress:function(){}},s={},a=null,f=[],u=[],c=[],d=[],p=0,l=0,v=0,g=0,m=0,b=0,x=!1,w=!1,h=!1,y=!1,E=!1,M=!1,A="down",O=[];function I(e){var t=0;if(e.offsetParent)do{t+=e.offsetTop,e=e.offsetParent}while(e);return t<0?0:t}function S(e){return+e.getAttribute("data-scrollama-index")}function H(){window.pageYOffset>m?A="down":window.pageYOffset<m&&(A="up"),m=window.pageYOffset}function P(e){s[e]&&s[e].forEach(function(e){return e.disconnect()})}function q(){var e,t;v=window.innerHeight,e=document.body,t=document.documentElement,g=Math.max(e.scrollHeight,e.offsetHeight,t.clientHeight,t.scrollHeight,t.offsetHeight),l=p*v,x&&(u=f.map(function(e){return e.offsetHeight}),c=f.map(I),w&&F()),h&&r({id:a,stepOffsetHeight:u,offsetMargin:l,offsetVal:p})}function _(e){if(e&&!w)return x&&F(),w=!0,!0;t.forEach(P),w=!1}function C(e,t){var n=S(e);void 0!==t&&(d[n].progress=t);var r={element:e,index:n,progress:d[n].progress};"enter"===d[n].state&&i.stepProgress(r)}function V(e,t){if("above"===t)for(var n=0;n<e;n++){var r=d[n];"enter"!==r.state&&"down"!==r.direction?(k(f[n],"down",!1),j(f[n],"down")):"enter"===r.state&&j(f[n],"down")}else if("below"===t)for(var o=d.length-1;o>e;o--){var i=d[o];"enter"===i.state&&j(f[o],"up"),"down"===i.direction&&(k(f[o],"up",!1),j(f[o],"up"))}}function k(e,t,n){void 0===n&&(n=!0);var r=S(e),s={element:e,index:r,direction:t};d[r].direction=t,d[r].state="enter",E&&n&&"down"===t&&V(r,"above"),E&&n&&"up"===t&&V(r,"below"),i.stepEnter&&!O[r]&&(i.stepEnter(s,d),h&&o({id:a,index:r,state:"enter"}),M&&(O[r]=!0)),y&&C(e)}function j(e,t){var n=S(e),r={element:e,index:n,direction:t};y&&("down"===t&&d[n].progress<1?C(e,1):"up"===t&&d[n].progress>0&&C(e,0)),d[n].direction=t,d[n].state="exit",i.stepExit(r,d),h&&o({id:a,index:n,state:"exit"})}function T(e){var t=e[0];H();var n=t.isIntersecting,r=t.boundingClientRect,o=t.target,i=r.top,s=r.bottom,a=i-l,f=s-l,u=S(o),c=d[u];n&&a<=0&&f>=0&&"down"===A&&"enter"!==c.state&&k(o,A),!n&&a>0&&"up"===A&&"enter"===c.state&&j(o,A)}function R(e){var t=e[0];H();var n=t.isIntersecting,r=t.boundingClientRect,o=t.target,i=r.top,s=r.bottom,a=i-l,f=s-l,u=S(o),c=d[u];n&&a<=0&&f>=0&&"up"===A&&"enter"!==c.state&&k(o,A),!n&&f<0&&"down"===A&&"enter"===c.state&&j(o,A)}function z(e){var t=e[0];H();var n=t.isIntersecting,r=t.target,o=S(r),i=d[o];n&&"down"===A&&"down"!==i.direction&&"enter"!==i.state&&(k(r,"down"),j(r,"down"))}function B(e){var t=e[0];H();var n=t.isIntersecting,r=t.target,o=S(r),i=d[o];n&&"up"===A&&"down"===i.direction&&"enter"!==i.state&&(k(r,"up"),j(r,"up"))}function N(e){var t=e[0];H();var n=t.isIntersecting,r=t.intersectionRatio,o=t.boundingClientRect,i=t.target,s=o.bottom;n&&s-l>=0&&C(i,+r.toFixed(3))}function Y(){s.stepProgress=f.map(function(e,t){var n=u[t]-l+"px 0px "+(-v+l)+"px 0px",r=function(e){for(var t=Math.ceil(e/b),n=[],r=1/t,o=0;o<t;o++)n.push(o*r);return n}(u[t]),o=new IntersectionObserver(N,{rootMargin:n,threshold:r});return o.observe(e),o})}function F(){t.forEach(P),s.viewportAbove=f.map(function(e,t){var n=g-c[t],r=l-v-u[t],o=new IntersectionObserver(z,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),s.viewportBelow=f.map(function(e,t){var n=-l-u[t],r=l-v+u[t]+g,o=new IntersectionObserver(B,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),s.stepAbove=f.map(function(e,t){var n=-l+u[t],r=new IntersectionObserver(T,{rootMargin:n+"px 0px "+(l-v)+"px 0px"});return r.observe(e),r}),s.stepAbove=f.map(function(e,t){var n=-l,r=l-v+u[t],o=new IntersectionObserver(R,{rootMargin:n+"px 0px "+r+"px 0px"});return o.observe(e),o}),y&&Y()}var D={};return D.setup=function(t){var r=t.step,o=t.offset;void 0===o&&(o=.5);var i=t.progress;void 0===i&&(i=!1);var s=t.threshold;void 0===s&&(s=4);var u=t.debug;void 0===u&&(u=!1);var c=t.order;void 0===c&&(c=!0);var l,v,g,m,w,A=t.once;return void 0===A&&(A=!1),v=(l="abcdefghijklmnopqrstuv").length,g=Date.now(),a=""+[0,0,0].map(function(e){return l[Math.floor(Math.random()*v)]}).join("")+g,m=r,void 0===w&&(w=document),(f="string"==typeof m?e(w.querySelectorAll(m)):m instanceof Element?e([m]):m instanceof NodeList?e(m):m instanceof Array?m:[]).length?(h=u,y=i,E=c,M=A,D.offsetTrigger(o),b=Math.max(1,+s),x=!0,h&&n({id:a,stepEl:f,offsetVal:p}),f.forEach(function(e,t){return e.setAttribute("data-scrollama-index",t)}),d=f.map(function(){return{direction:null,state:null,progress:0}}),q(),D.enable(),D):(console.error("scrollama error: no step elements"),D)},D.resize=function(){return q(),D},D.enable=function(){return _(!0),D},D.disable=function(){return _(!1),D},D.destroy=function(){_(!1),Object.keys(i).forEach(function(e){return i[e]=null}),Object.keys(s).forEach(function(e){return s[e]=null})},D.offsetTrigger=function(e){return e&&!isNaN(e)?(p=Math.min(Math.max(0,e),1),D):p},D.onStepEnter=function(e){return"function"==typeof e?i.stepEnter=e:console.error("scrollama error: onStepEnter requires a function"),D},D.onStepExit=function(e){return"function"==typeof e?i.stepExit=e:console.error("scrollama error: onStepExit requires a function"),D},D.onStepProgress=function(e){return"function"==typeof e?i.stepProgress=e:console.error("scrollama error: onStepProgress requires a function"),D},D}}); |
/*! | ||
* Stickyfill – `position: sticky` polyfill | ||
* v. 2.0.2 | https://github.com/wilddeer/stickyfill | ||
* v. 2.1.0 | https://github.com/wilddeer/stickyfill | ||
* MIT License | ||
*/ | ||
!function(a,b){"use strict";function c(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function d(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}function e(a){return parseFloat(a)||0}function f(a){for(var b=0;a;)b+=a.offsetTop,a=a.offsetParent;return b}function g(){function c(){a.pageXOffset!=k.left?(k.top=a.pageYOffset,k.left=a.pageXOffset,n.refreshAll()):a.pageYOffset!=k.top&&(k.top=a.pageYOffset,k.left=a.pageXOffset,l.forEach(function(a){return a._recalcPosition()}))}function d(){f=setInterval(function(){l.forEach(function(a){return a._fastCheck()})},500)}function e(){clearInterval(f)}c(),a.addEventListener("scroll",c),a.addEventListener("resize",n.refreshAll),a.addEventListener("orientationchange",n.refreshAll);var f=void 0,g=void 0,h=void 0;"hidden"in b?(g="hidden",h="visibilitychange"):"webkitHidden"in b&&(g="webkitHidden",h="webkitvisibilitychange"),h?(b[g]||d(),b.addEventListener(h,function(){b[g]?e():d()})):d()}var h=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),i=!1;a.getComputedStyle?!function(){var a=b.createElement("div");["","-webkit-","-moz-","-ms-"].some(function(b){try{a.style.position=b+"sticky"}catch(a){}return""!=a.style.position})&&(i=!0)}():i=!0;var j="undefined"!=typeof ShadowRoot,k={top:null,left:null},l=[],m=function(){function g(a){if(c(this,g),!(a instanceof HTMLElement))throw new Error("First argument must be HTMLElement");if(l.some(function(b){return b._node===a}))throw new Error("Stickyfill is already applied to this node");this._node=a,this._stickyMode=null,this._active=!1,l.push(this),this.refresh()}return h(g,[{key:"refresh",value:function(){if(!i&&!this._removed){this._active&&this._deactivate();var c=this._node,g=getComputedStyle(c);if(!isNaN(parseFloat(g.top))&&"table-cell"!=g.display&&"none"!=g.display){this._active=!0;var h=c.parentNode,k=j&&h instanceof ShadowRoot?h.host:h,l=c.getBoundingClientRect(),m=k.getBoundingClientRect(),n=getComputedStyle(k);this._parent={node:k,styles:{position:k.style.position},offsetHeight:k.offsetHeight},this._offsetToWindow={left:l.left,right:b.documentElement.clientWidth-l.right},this._offsetToParent={top:l.top-m.top-e(n.borderTopWidth),left:l.left-m.left-e(n.borderLeftWidth),right:-l.right+m.right-e(n.borderRightWidth)},this._styles={position:c.style.position,top:c.style.top,bottom:c.style.bottom,left:c.style.left,right:c.style.right,width:c.style.width,marginTop:c.style.marginTop,marginLeft:c.style.marginLeft,marginRight:c.style.marginRight};var o=e(g.top);this._limits={start:l.top+a.pageYOffset-o,end:m.top+a.pageYOffset+k.offsetHeight-e(n.borderBottomWidth)-c.offsetHeight-o-e(g.marginBottom)};var p=n.position;"absolute"!=p&&"relative"!=p&&(k.style.position="relative");var q=this._clone={};q.node=b.createElement("div"),d(q.node.style,{width:l.right-l.left+"px",height:l.bottom-l.top+"px",marginTop:g.marginTop,marginBottom:g.marginBottom,marginLeft:g.marginLeft,marginRight:g.marginRight,cssFloat:g.cssFloat,padding:0,border:0,borderSpacing:0,fontSize:"1em",position:"static"}),h.insertBefore(q.node,c),q.docOffsetTop=f(q.node),this._recalcPosition()}}}},{key:"_recalcPosition",value:function(){if(this._active&&!this._removed){var a=k.top<=this._limits.start?"start":k.top>=this._limits.end?"end":"middle";if(this._stickyMode!=a){switch(a){case"start":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:this._offsetToParent.top+"px",bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"middle":d(this._node.style,{position:"fixed",left:this._offsetToWindow.left+"px",right:this._offsetToWindow.right+"px",top:this._styles.top,bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"end":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:"auto",bottom:0,width:"auto",marginLeft:0,marginRight:0})}this._stickyMode=a}}}},{key:"_fastCheck",value:function(){this._active&&!this._removed&&(Math.abs(f(this._clone.node)-this._clone.docOffsetTop)>1||Math.abs(this._parent.node.offsetHeight-this._parent.offsetHeight)>1)&&this.refresh()}},{key:"_deactivate",value:function(){var a=this;this._active&&!this._removed&&(this._clone.node.parentNode.removeChild(this._clone.node),delete this._clone,d(this._node.style,this._styles),delete this._styles,l.some(function(b){return b!==a&&b._parent&&b._parent.node===a._parent.node})||d(this._parent.node.style,this._parent.styles),delete this._parent,this._stickyMode=null,this._active=!1,delete this._offsetToWindow,delete this._offsetToParent,delete this._limits)}},{key:"remove",value:function(){var a=this;this._deactivate(),l.some(function(b,c){if(b._node===a._node)return l.splice(c,1),!0}),this._removed=!0}}]),g}(),n={stickies:l,Sticky:m,addOne:function(a){if(!(a instanceof HTMLElement)){if(!a.length||!a[0])return;a=a[0]}for(var b=0;b<l.length;b++)if(l[b]._node===a)return l[b];return new m(a)},add:function(a){if(a instanceof HTMLElement&&(a=[a]),a.length){for(var b=[],c=function(c){var d=a[c];return d instanceof HTMLElement?l.some(function(a){if(a._node===d)return b.push(a),!0})?"continue":void b.push(new m(d)):(b.push(void 0),"continue")},d=0;d<a.length;d++){c(d)}return b}},refreshAll:function(){l.forEach(function(a){return a.refresh()})},removeOne:function(a){if(!(a instanceof HTMLElement)){if(!a.length||!a[0])return;a=a[0]}l.some(function(b){if(b._node===a)return b.remove(),!0})},remove:function(a){if(a instanceof HTMLElement&&(a=[a]),a.length)for(var b=function(b){var c=a[b];l.some(function(a){if(a._node===c)return a.remove(),!0})},c=0;c<a.length;c++)b(c)},removeAll:function(){for(;l.length;)l[0].remove()}};i||g(),"undefined"!=typeof module&&module.exports?module.exports=n:a.Stickyfill=n}(window,document); | ||
!function(a,b){"use strict";function c(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function d(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}function e(a){return parseFloat(a)||0}function f(a){for(var b=0;a;)b+=a.offsetTop,a=a.offsetParent;return b}function g(){function c(){a.pageXOffset!=m.left?(m.top=a.pageYOffset,m.left=a.pageXOffset,p.refreshAll()):a.pageYOffset!=m.top&&(m.top=a.pageYOffset,m.left=a.pageXOffset,n.forEach(function(a){return a._recalcPosition()}))}function d(){f=setInterval(function(){n.forEach(function(a){return a._fastCheck()})},500)}function e(){clearInterval(f)}if(!k){k=!0,c(),a.addEventListener("scroll",c),a.addEventListener("resize",p.refreshAll),a.addEventListener("orientationchange",p.refreshAll);var f=void 0,g=void 0,h=void 0;"hidden"in b?(g="hidden",h="visibilitychange"):"webkitHidden"in b&&(g="webkitHidden",h="webkitvisibilitychange"),h?(b[g]||d(),b.addEventListener(h,function(){b[g]?e():d()})):d()}}var h=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),i=!1,j="undefined"!=typeof a;j&&a.getComputedStyle?!function(){var a=b.createElement("div");["","-webkit-","-moz-","-ms-"].some(function(b){try{a.style.position=b+"sticky"}catch(a){}return""!=a.style.position})&&(i=!0)}():i=!0;var k=!1,l="undefined"!=typeof ShadowRoot,m={top:null,left:null},n=[],o=function(){function g(a){if(c(this,g),!(a instanceof HTMLElement))throw new Error("First argument must be HTMLElement");if(n.some(function(b){return b._node===a}))throw new Error("Stickyfill is already applied to this node");this._node=a,this._stickyMode=null,this._active=!1,n.push(this),this.refresh()}return h(g,[{key:"refresh",value:function(){if(!i&&!this._removed){this._active&&this._deactivate();var c=this._node,g=getComputedStyle(c),h={position:g.position,top:g.top,display:g.display,marginTop:g.marginTop,marginBottom:g.marginBottom,marginLeft:g.marginLeft,marginRight:g.marginRight,cssFloat:g.cssFloat};if(!isNaN(parseFloat(h.top))&&"table-cell"!=h.display&&"none"!=h.display){this._active=!0;var j=c.style.position;"sticky"!=g.position&&"-webkit-sticky"!=g.position||(c.style.position="static");var k=c.parentNode,m=l&&k instanceof ShadowRoot?k.host:k,n=c.getBoundingClientRect(),o=m.getBoundingClientRect(),p=getComputedStyle(m);this._parent={node:m,styles:{position:m.style.position},offsetHeight:m.offsetHeight},this._offsetToWindow={left:n.left,right:b.documentElement.clientWidth-n.right},this._offsetToParent={top:n.top-o.top-e(p.borderTopWidth),left:n.left-o.left-e(p.borderLeftWidth),right:-n.right+o.right-e(p.borderRightWidth)},this._styles={position:j,top:c.style.top,bottom:c.style.bottom,left:c.style.left,right:c.style.right,width:c.style.width,marginTop:c.style.marginTop,marginLeft:c.style.marginLeft,marginRight:c.style.marginRight};var q=e(h.top);this._limits={start:n.top+a.pageYOffset-q,end:o.top+a.pageYOffset+m.offsetHeight-e(p.borderBottomWidth)-c.offsetHeight-q-e(h.marginBottom)};var r=p.position;"absolute"!=r&&"relative"!=r&&(m.style.position="relative"),this._recalcPosition();var s=this._clone={};s.node=b.createElement("div"),d(s.node.style,{width:n.right-n.left+"px",height:n.bottom-n.top+"px",marginTop:h.marginTop,marginBottom:h.marginBottom,marginLeft:h.marginLeft,marginRight:h.marginRight,cssFloat:h.cssFloat,padding:0,border:0,borderSpacing:0,fontSize:"1em",position:"static"}),k.insertBefore(s.node,c),s.docOffsetTop=f(s.node)}}}},{key:"_recalcPosition",value:function(){if(this._active&&!this._removed){var a=m.top<=this._limits.start?"start":m.top>=this._limits.end?"end":"middle";if(this._stickyMode!=a){switch(a){case"start":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:this._offsetToParent.top+"px",bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"middle":d(this._node.style,{position:"fixed",left:this._offsetToWindow.left+"px",right:this._offsetToWindow.right+"px",top:this._styles.top,bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"end":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:"auto",bottom:0,width:"auto",marginLeft:0,marginRight:0})}this._stickyMode=a}}}},{key:"_fastCheck",value:function(){this._active&&!this._removed&&(Math.abs(f(this._clone.node)-this._clone.docOffsetTop)>1||Math.abs(this._parent.node.offsetHeight-this._parent.offsetHeight)>1)&&this.refresh()}},{key:"_deactivate",value:function(){var a=this;this._active&&!this._removed&&(this._clone.node.parentNode.removeChild(this._clone.node),delete this._clone,d(this._node.style,this._styles),delete this._styles,n.some(function(b){return b!==a&&b._parent&&b._parent.node===a._parent.node})||d(this._parent.node.style,this._parent.styles),delete this._parent,this._stickyMode=null,this._active=!1,delete this._offsetToWindow,delete this._offsetToParent,delete this._limits)}},{key:"remove",value:function(){var a=this;this._deactivate(),n.some(function(b,c){if(b._node===a._node)return n.splice(c,1),!0}),this._removed=!0}}]),g}(),p={stickies:n,Sticky:o,forceSticky:function(){i=!1,g(),this.refreshAll()},addOne:function(a){if(!(a instanceof HTMLElement)){if(!a.length||!a[0])return;a=a[0]}for(var b=0;b<n.length;b++)if(n[b]._node===a)return n[b];return new o(a)},add:function(a){if(a instanceof HTMLElement&&(a=[a]),a.length){for(var b=[],c=function(c){var d=a[c];return d instanceof HTMLElement?n.some(function(a){if(a._node===d)return b.push(a),!0})?"continue":void b.push(new o(d)):(b.push(void 0),"continue")},d=0;d<a.length;d++){c(d)}return b}},refreshAll:function(){n.forEach(function(a){return a.refresh()})},removeOne:function(a){if(!(a instanceof HTMLElement)){if(!a.length||!a[0])return;a=a[0]}n.some(function(b){if(b._node===a)return b.remove(),!0})},remove:function(a){if(a instanceof HTMLElement&&(a=[a]),a.length)for(var b=function(b){var c=a[b];n.some(function(a){if(a._node===c)return a.remove(),!0})},c=0;c<a.length;c++)b(c)},removeAll:function(){for(;n.length;)n[0].remove()}};i||g(),"undefined"!=typeof module&&module.exports?module.exports=p:j&&(a.Stickyfill=p)}(window,document); |
{ | ||
"name": "scrollama", | ||
"version": "1.4.4", | ||
"version": "2.0.0", | ||
"description": "Lightweight scrollytelling library using IntersectionObserver", | ||
@@ -5,0 +5,0 @@ "main": "build/scrollama.js", |
109
README.md
@@ -10,9 +10,7 @@ ###### scrollama.js | ||
**Notes: As of version 1.4.0, you must manually add the IntersectionObserver polyfill for cross-browser support. See [installation](https://github.com/russellgoldenberg/scrollama#installation) for details. Although it remains in the API (for now), it is recommended to use the CSS property `position: sticky;` instead of `.onContainerEnter` and `.onContainerExit`. [Full blog post here](https://pudding.cool/process/scrollytelling-sticky/).** | ||
#### Important Changes | ||
As seen on [The Pudding](https://pudding.cool/): | ||
- **Version 2.0.0+**: `.onContainerEnter` and `.onContainerExit` have been deprecated in favor of CSS property `position: sticky;`. [How to use position sticky.](https://pudding.cool/process/scrollytelling-sticky/) | ||
- **Version 1.4.0+**: you must manually add the IntersectionObserver polyfill for cross-browser support. See [installation](https://github.com/russellgoldenberg/scrollama#installation) for details. | ||
- [What is a Superteam in the NBA?](https://pudding.cool/2017/10/superteams/) | ||
- [What City is the Microbrew Capital of the US?](https://pudding.cool/2017/04/beer/) | ||
[Jump to examples.](https://github.com/russellgoldenberg/scrollama#examples) | ||
@@ -26,7 +24,3 @@ | ||
[IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) | ||
to handle element position detection. It offers an opinionated (but popular) | ||
scrollytelling pattern to reduce more involved DOM calculations. The sticky | ||
graphic pattern (enter-stick-exit) can be seen below. Check out my | ||
[blog post](https://pudding.cool/process/introducing-scrollama) for a deeper | ||
introduction. | ||
to handle element position detection. | ||
@@ -44,13 +38,6 @@ [![scrollytelling pattern](https://thumbs.gfycat.com/FearfulHotArabianoryx-size_restricted.gif)](https://pudding.cool/process/how-to-implement-scrollytelling) | ||
incremental step progress callback | ||
- [Sticky Graphic v1a (CSS, position sticky)](https://russellgoldenberg.github.io/scrollama/sticky-css) - | ||
using CSS vertically center chart, and position sticky (+ polyfill) for | ||
sticking. | ||
- [Sticky Graphic v1b (JS, position sticky)](https://russellgoldenberg.github.io/scrollama/sticky-js) - | ||
using JS vertically center chart, and position sticky (+ polyfill) for | ||
sticking. Added bonus ability to start chart at top of steps then vertically. | ||
- [Sticky Graphic v2a (CSS, position fixed)](https://russellgoldenberg.github.io/scrollama/fixed-css) - | ||
using CSS vertically center chart, and position fixed and absolute for | ||
sticking. | ||
- [Sticky Graphic v2b (JS, position fixed)](https://russellgoldenberg.github.io/scrollama/fixed-js) - | ||
using read position fixed and absolute for sticking. | ||
- [Sticky Graphic (Side by Side)](https://russellgoldenberg.github.io/scrollama/sticky-side) - | ||
using CSS `position: sticky;` to create a fixed graphic to the side of the text. | ||
- [Sticky Graphic (Overlay)](https://russellgoldenberg.github.io/scrollama/sticky-overlay) - | ||
using CSS `position: sticky;` to create a fixed graphic with fullscreen graphic with text overlayed. | ||
@@ -64,4 +51,4 @@ ### Installation | ||
```html | ||
<script src='https://unpkg.com/intersection-observer@0.5.0/intersection-observer.js'></script> | ||
<script src='https://unpkg.com/scrollama'></script> | ||
<script src="https://unpkg.com/intersection-observer@0.5.1/intersection-observer.js"></script> | ||
<script src="https://unpkg.com/scrollama"></script> | ||
``` | ||
@@ -72,3 +59,3 @@ | ||
```sh | ||
npm install scrollama intersection-observer | ||
npm install scrollama intersection-observer --save | ||
``` | ||
@@ -98,5 +85,5 @@ | ||
<!--you don't need the "data-step" attr, but can be useful for storing instructions for JS --> | ||
<div class='step' data-step='a'></div> | ||
<div class='step' data-step='b'></div> | ||
<div class='step' data-step='c'></div> | ||
<div class="step" data-step="a"></div> | ||
<div class="step" data-step="b"></div> | ||
<div class="step" data-step="c"></div> | ||
``` | ||
@@ -117,42 +104,2 @@ | ||
#### Sticky Graphic | ||
**Update:** I recommend using the CSS property `position:sticky;`. You can simply use the triggers like above, and let the CSS handle everything else. [Full blog post here](https://pudding.cool/process/scrollytelling-sticky/). | ||
To implement the sticky graphic scrollytelling pattern, you need the following | ||
three elements (container, graphic, steps). The structure should look like: | ||
```html | ||
<!-- container = ".scroll" --> | ||
<div class='scroll'> | ||
<!-- graphic = ".scroll__graphic" --> | ||
<div class='scroll__graphic'> | ||
<!--graphic / chart code here--> | ||
</div> | ||
<div class='scroll__text'> | ||
<!-- steps = ".step" --> | ||
<div class='step' data-step='a'></div> | ||
<div class='step' data-step='b'></div> | ||
<div class='step' data-step='c'></div> | ||
</div> | ||
</div> | ||
``` | ||
```js | ||
// instantiate the scrollama | ||
const scroller = scrollama(); | ||
// setup the instance, pass callback functions | ||
scroller | ||
.setup({ | ||
step: '.scroll__text .step', // required | ||
container: '.scroll', // required (for sticky) | ||
graphic: '.scroll__graphic' // required (for sticky) | ||
}) | ||
.onStepEnter(handleStepEnter) | ||
.onStepExit(handleStepExit) | ||
.onContainerEnter(handleContainerEnter) | ||
.onContainerExit(handleContainerExit); | ||
``` | ||
### API | ||
@@ -166,6 +113,2 @@ | ||
**required** | ||
- `container` (string): Selector (or element) for the element that contains everything for | ||
the scroller. **optional** | ||
- `graphic` (string): Selector (or element) for the graphic element that will become fixed. | ||
**optional** | ||
- `offset` (number, 0 - 1): How far from the top of the viewport to trigger a | ||
@@ -175,5 +118,5 @@ step. **(default: 0.5)** | ||
not. **(default: false)** | ||
- `threshold` (number, 1+): The granularity of the progress interval, in pixels (smaller = more granular updates). **(default: 4)** | ||
- `order` (boolean): Whether to preserve step triggering order if they fire out of sync (eg. ensure step 2 enters after 1 exits). **(default: true)** | ||
- `once` (boolean): Only trigger the step to enter once then remove listener. **default: false** | ||
- `threshold` (number, 1+): The granularity of the progress interval in pixels (smaller = more granular). **(default: 4)** | ||
- `order` (boolean): Fire previous step triggers if they were jumped. **(default: true)** | ||
- `once` (boolean): Only trigger the step to enter once then remove listener. **(default: false)** | ||
- `debug` (boolean): Whether to show visual debugging tools or not. **(default: | ||
@@ -220,20 +163,2 @@ false)** | ||
#### scrollama.onContainerEnter(callback) | ||
Callback that fires when the top of container becomes flush with viewport _or_ | ||
the graphic becomes fully in view coming from the bottom of the container. | ||
The argument of the callback is an object: `{ direction: string }` | ||
`direction`: 'up' or 'down' | ||
#### scrollama.onContainerExit(callback) | ||
Callback that fires when the top of container goes below viewport _or_ the | ||
graphic becomes not full in view leaving the bottom of the container. | ||
The argument of the callback is an object: `{ direction: string }` | ||
`direction`: 'up' or 'down' | ||
#### scrollama.offsetTrigger([number]) | ||
@@ -240,0 +165,0 @@ |
555
src/init.js
@@ -1,43 +0,50 @@ | ||
import { select, selectAll } from './dom'; | ||
import { selectAll } from './dom'; | ||
import * as bug from './debug'; | ||
function scrollama() { | ||
const ZERO_MOE = 1; // zero with some rounding margin of error | ||
const margin = {}; | ||
const callback = {}; | ||
const OBSERVER_NAMES = [ | ||
'stepAbove', | ||
'stepBelow', | ||
'stepProgress', | ||
'viewportAbove', | ||
'viewportBelow' | ||
]; | ||
const cb = { | ||
stepEnter: () => {}, | ||
stepExit: () => {}, | ||
stepProgress: () => {} | ||
}; | ||
const io = {}; | ||
let containerEl = null; | ||
let graphicEl = null; | ||
let stepEl = null; | ||
let id = null; | ||
let stepEl = []; | ||
let stepOffsetHeight = []; | ||
let stepOffsetTop = []; | ||
let stepStates = []; | ||
let id = null; | ||
let offsetVal = 0; | ||
let offsetMargin = 0; | ||
let vh = 0; | ||
let ph = 0; | ||
let stepOffsetHeight = null; | ||
let stepOffsetTop = null; | ||
let bboxGraphic = null; | ||
let viewH = 0; | ||
let pageH = 0; | ||
let previousYOffset = 0; | ||
let progressThreshold = 0; | ||
let isReady = false; | ||
let isEnabled = false; | ||
let debugMode = false; | ||
let isDebug = false; | ||
let progressMode = false; | ||
let progressThreshold = 0; | ||
let preserveOrder = false; | ||
let triggerOnce = false; | ||
let stepStates = null; | ||
let containerState = null; | ||
let previousYOffset = -1; | ||
let direction = null; | ||
let direction = 'down'; | ||
const exclude = []; | ||
// HELPERS | ||
function generateId() { | ||
/*** HELPERS ***/ | ||
function generateInstanceID() { | ||
const a = 'abcdefghijklmnopqrstuv'; | ||
const l = a.length; | ||
const t = new Date().getTime(); | ||
const t = Date.now(); | ||
const r = [0, 0, 0].map(d => a[Math.floor(Math.random() * l)]).join(''); | ||
@@ -87,18 +94,19 @@ return `${r}${t}`; | ||
function disconnectObserver(name) { | ||
if (io[name]) io[name].forEach(d => d.disconnect()); | ||
} | ||
function handleResize() { | ||
vh = window.innerHeight; | ||
ph = getPageHeight(); | ||
viewH = window.innerHeight; | ||
pageH = getPageHeight(); | ||
bboxGraphic = graphicEl ? graphicEl.getBoundingClientRect() : null; | ||
offsetMargin = offsetVal * viewH; | ||
offsetMargin = offsetVal * vh; | ||
if (isReady) { | ||
stepOffsetHeight = stepEl.map(el => el.offsetHeight); | ||
stepOffsetTop = stepEl.map(getOffsetTop); | ||
if (isEnabled) updateIO(); | ||
} | ||
stepOffsetHeight = stepEl ? stepEl.map(el => el.offsetHeight) : []; | ||
stepOffsetTop = stepEl ? stepEl.map(getOffsetTop) : []; | ||
if (isEnabled && isReady) updateIO(); | ||
if (debugMode) | ||
bug.update({ id, stepOffsetHeight, offsetMargin, offsetVal }); | ||
if (isDebug) bug.update({ id, stepOffsetHeight, offsetMargin, offsetVal }); | ||
} | ||
@@ -110,12 +118,6 @@ | ||
isEnabled = true; | ||
} else if (!enable) { | ||
if (io.top) io.top.disconnect(); | ||
if (io.bottom) io.bottom.disconnect(); | ||
if (io.stepAbove) io.stepAbove.forEach(d => d.disconnect()); | ||
if (io.stepBelow) io.stepBelow.forEach(d => d.disconnect()); | ||
if (io.stepProgress) io.stepProgress.forEach(d => d.disconnect()); | ||
if (io.viewportAbove) io.viewportAbove.forEach(d => d.disconnect()); | ||
if (io.viewportBelow) io.viewportBelow.forEach(d => d.disconnect()); | ||
isEnabled = false; | ||
return true; | ||
} | ||
OBSERVER_NAMES.forEach(disconnectObserver); | ||
isEnabled = false; | ||
} | ||
@@ -133,3 +135,12 @@ | ||
// NOTIFY CALLBACKS | ||
/*** NOTIFY CALLBACKS ***/ | ||
function notifyStepProgress(element, progress) { | ||
const index = getIndex(element); | ||
if (progress !== undefined) stepStates[index].progress = progress; | ||
const resp = { element, index, progress: stepStates[index].progress }; | ||
if (stepStates[index].state === 'enter') cb.stepProgress(resp); | ||
} | ||
function notifyOthers(index, location) { | ||
@@ -140,7 +151,10 @@ if (location === 'above') { | ||
const ss = stepStates[i]; | ||
if (ss.state === 'enter') notifyStepExit(stepEl[i], 'down'); | ||
if (ss.direction === 'up') { | ||
if (ss.state !== 'enter' && ss.direction !== 'down') { | ||
notifyStepEnter(stepEl[i], 'down', false); | ||
notifyStepExit(stepEl[i], 'down'); | ||
} | ||
} else if (ss.state === 'enter') notifyStepExit(stepEl[i], 'down'); | ||
// else if (ss.direction === 'up') { | ||
// notifyStepEnter(stepEl[i], 'down', false); | ||
// notifyStepExit(stepEl[i], 'down'); | ||
// } | ||
} | ||
@@ -168,3 +182,2 @@ } else if (location === 'below') { | ||
stepStates[index].state = 'enter'; | ||
if (preserveOrder && check && direction === 'down') | ||
@@ -176,16 +189,9 @@ notifyOthers(index, 'above'); | ||
if ( | ||
callback.stepEnter && | ||
typeof callback.stepEnter === 'function' && | ||
!exclude[index] | ||
) { | ||
callback.stepEnter(resp, stepStates); | ||
if (debugMode) bug.notifyStep({ id, index, state: 'enter' }); | ||
if (cb.stepEnter && !exclude[index]) { | ||
cb.stepEnter(resp, stepStates); | ||
if (isDebug) bug.notifyStep({ id, index, state: 'enter' }); | ||
if (triggerOnce) exclude[index] = true; | ||
} | ||
if (progressMode) { | ||
if (direction === 'down') notifyStepProgress(element, 0); | ||
else notifyStepProgress(element, 1); | ||
} | ||
if (progressMode) notifyStepProgress(element); | ||
} | ||
@@ -197,2 +203,9 @@ | ||
if (progressMode) { | ||
if (direction === 'down' && stepStates[index].progress < 1) | ||
notifyStepProgress(element, 1); | ||
else if (direction === 'up' && stepStates[index].progress > 0) | ||
notifyStepProgress(element, 0); | ||
} | ||
// store most recent trigger | ||
@@ -202,98 +215,73 @@ stepStates[index].direction = direction; | ||
if (progressMode) { | ||
if (direction === 'down') notifyStepProgress(element, 1); | ||
else notifyStepProgress(element, 0); | ||
} | ||
if (callback.stepExit && typeof callback.stepExit === 'function') { | ||
callback.stepExit(resp, stepStates); | ||
if (debugMode) bug.notifyStep({ id, index, state: 'exit' }); | ||
} | ||
cb.stepExit(resp, stepStates); | ||
if (isDebug) bug.notifyStep({ id, index, state: 'exit' }); | ||
} | ||
function notifyStepProgress(element, progress) { | ||
const index = getIndex(element); | ||
const resp = { element, index, progress }; | ||
if (callback.stepProgress && typeof callback.stepProgress === 'function') | ||
callback.stepProgress(resp); | ||
} | ||
/*** OBSERVER - INTERSECT HANDLING ***/ | ||
// this is good for entering while scrolling down + leaving while scrolling up | ||
function intersectStepAbove([entry]) { | ||
updateDirection(); | ||
const { isIntersecting, boundingClientRect, target } = entry; | ||
function notifyContainerEnter() { | ||
const resp = { direction }; | ||
containerState.direction = direction; | ||
containerState.state = 'enter'; | ||
// bottom = bottom edge of element from top of viewport | ||
// bottomAdjusted = bottom edge of element from trigger | ||
const { top, bottom } = boundingClientRect; | ||
const topAdjusted = top - offsetMargin; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
// entering above is only when topAdjusted is negative | ||
// and bottomAdjusted is positive | ||
if ( | ||
callback.containerEnter && | ||
typeof callback.containerEnter === 'function' | ||
isIntersecting && | ||
topAdjusted <= 0 && | ||
bottomAdjusted >= 0 && | ||
direction === 'down' && | ||
ss.state !== 'enter' | ||
) | ||
callback.containerEnter(resp); | ||
} | ||
notifyStepEnter(target, direction); | ||
function notifyContainerExit() { | ||
const resp = { direction }; | ||
containerState.direction = direction; | ||
containerState.state = 'exit'; | ||
if (callback.containerExit && typeof callback.containerExit === 'function') | ||
callback.containerExit(resp); | ||
// exiting from above is when topAdjusted is positive and not intersecting | ||
if ( | ||
!isIntersecting && | ||
topAdjusted > 0 && | ||
direction === 'up' && | ||
ss.state === 'enter' | ||
) | ||
notifyStepExit(target, direction); | ||
} | ||
// OBSERVER - INTERSECT HANDLING | ||
// if TOP edge of step crosses threshold, | ||
// bottom must be > 0 which means it is on "screen" (shifted by offset) | ||
function intersectStepAbove(entries) { | ||
// this is good for entering while scrolling up + leaving while scrolling down | ||
function intersectStepBelow([entry]) { | ||
updateDirection(); | ||
entries.forEach(entry => { | ||
const { isIntersecting, boundingClientRect, target } = entry; | ||
const { isIntersecting, boundingClientRect, target } = entry; | ||
// bottom is how far bottom edge of el is from top of viewport | ||
const { bottom, height } = boundingClientRect; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
// bottom = bottom edge of element from top of viewport | ||
// bottomAdjusted = bottom edge of element from trigger | ||
const { top, bottom } = boundingClientRect; | ||
const topAdjusted = top - offsetMargin; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
if (bottomAdjusted >= -ZERO_MOE) { | ||
if (isIntersecting && direction === 'down' && ss.state !== 'enter') | ||
notifyStepEnter(target, direction); | ||
else if (!isIntersecting && direction === 'up' && ss.state === 'enter') | ||
notifyStepExit(target, direction); | ||
else if ( | ||
!isIntersecting && | ||
bottomAdjusted >= height && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) { | ||
notifyStepExit(target, direction); | ||
} | ||
} | ||
}); | ||
} | ||
// entering below is only when bottomAdjusted is positive | ||
// and topAdjusted is positive | ||
if ( | ||
isIntersecting && | ||
topAdjusted <= 0 && | ||
bottomAdjusted >= 0 && | ||
direction === 'up' && | ||
ss.state !== 'enter' | ||
) | ||
notifyStepEnter(target, direction); | ||
function intersectStepBelow(entries) { | ||
updateDirection(); | ||
entries.forEach(entry => { | ||
const { isIntersecting, boundingClientRect, target } = entry; | ||
const { bottom, height } = boundingClientRect; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
if ( | ||
bottomAdjusted >= -ZERO_MOE && | ||
bottomAdjusted < height && | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, direction); | ||
} else if ( | ||
bottomAdjusted <= ZERO_MOE && | ||
!isIntersecting && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) { | ||
notifyStepExit(target, direction); | ||
} | ||
}); | ||
// exiting from above is when bottomAdjusted is negative and not intersecting | ||
if ( | ||
!isIntersecting && | ||
bottomAdjusted < 0 && | ||
direction === 'down' && | ||
ss.state === 'enter' | ||
) | ||
notifyStepExit(target, direction); | ||
} | ||
@@ -306,117 +294,60 @@ | ||
*/ | ||
function intersectViewportAbove(entries) { | ||
function intersectViewportAbove([entry]) { | ||
updateDirection(); | ||
entries.forEach(entry => { | ||
const { isIntersecting, target } = entry; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'down' && | ||
ss.state !== 'enter' && | ||
ss.direction !== 'down' | ||
) { | ||
notifyStepEnter(target, 'down'); | ||
notifyStepExit(target, 'down'); | ||
} | ||
}); | ||
} | ||
const { isIntersecting, target } = entry; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
function intersectViewportBelow(entries) { | ||
updateDirection(); | ||
entries.forEach(entry => { | ||
const { isIntersecting, target } = entry; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.state !== 'enter' && | ||
ss.direction !== 'up' | ||
) { | ||
notifyStepEnter(target, 'up'); | ||
notifyStepExit(target, 'up'); | ||
} | ||
}); | ||
if ( | ||
isIntersecting && | ||
direction === 'down' && | ||
ss.direction !== 'down' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, 'down'); | ||
notifyStepExit(target, 'down'); | ||
} | ||
} | ||
function intersectStepProgress(entries) { | ||
function intersectViewportBelow([entry]) { | ||
updateDirection(); | ||
entries.forEach( | ||
({ isIntersecting, intersectionRatio, boundingClientRect, target }) => { | ||
const { bottom } = boundingClientRect; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
if (isIntersecting && bottomAdjusted >= -ZERO_MOE) { | ||
notifyStepProgress(target, +intersectionRatio.toFixed(3)); | ||
} | ||
} | ||
); | ||
} | ||
function intersectTop(entries) { | ||
updateDirection(); | ||
const { isIntersecting, boundingClientRect } = entries[0]; | ||
const { top, bottom } = boundingClientRect; | ||
if (bottom > -ZERO_MOE) { | ||
if (isIntersecting) notifyContainerEnter(direction); | ||
else if (containerState.state === 'enter') notifyContainerExit(direction); | ||
const { isIntersecting, target } = entry; | ||
const index = getIndex(target); | ||
const ss = stepStates[index]; | ||
if ( | ||
isIntersecting && | ||
direction === 'up' && | ||
ss.direction === 'down' && | ||
ss.state !== 'enter' | ||
) { | ||
notifyStepEnter(target, 'up'); | ||
notifyStepExit(target, 'up'); | ||
} | ||
} | ||
function intersectBottom(entries) { | ||
function intersectStepProgress([entry]) { | ||
updateDirection(); | ||
const { isIntersecting, boundingClientRect } = entries[0]; | ||
const { top } = boundingClientRect; | ||
if (top < ZERO_MOE) { | ||
if (isIntersecting) notifyContainerEnter(direction); | ||
else if (containerState.state === 'enter') notifyContainerExit(direction); | ||
const { | ||
isIntersecting, | ||
intersectionRatio, | ||
boundingClientRect, | ||
target | ||
} = entry; | ||
const { bottom } = boundingClientRect; | ||
const bottomAdjusted = bottom - offsetMargin; | ||
if (isIntersecting && bottomAdjusted >= 0) { | ||
notifyStepProgress(target, +intersectionRatio.toFixed(3)); | ||
} | ||
} | ||
// OBSERVER - CREATION | ||
function updateTopIO() { | ||
if (io.top) io.top.unobserve(containerEl); | ||
const options = { | ||
root: null, | ||
rootMargin: `${vh}px 0px -${vh}px 0px`, | ||
threshold: 0 | ||
}; | ||
io.top = new IntersectionObserver(intersectTop, options); | ||
io.top.observe(containerEl); | ||
} | ||
function updateBottomIO() { | ||
if (io.bottom) io.bottom.unobserve(containerEl); | ||
const options = { | ||
root: null, | ||
rootMargin: `-${bboxGraphic.height}px 0px ${bboxGraphic.height}px 0px`, | ||
threshold: 0 | ||
}; | ||
io.bottom = new IntersectionObserver(intersectBottom, options); | ||
io.bottom.observe(containerEl); | ||
} | ||
// top edge | ||
function updateStepAboveIO() { | ||
if (io.stepAbove) io.stepAbove.forEach(d => d.disconnect()); | ||
io.stepAbove = stepEl.map((el, i) => { | ||
const marginTop = stepOffsetHeight[i]; | ||
const marginBottom = -vh + offsetMargin; | ||
/*** OBSERVER - CREATION ***/ | ||
// jump into viewport | ||
function updateViewportAboveIO() { | ||
io.viewportAbove = stepEl.map((el, i) => { | ||
const marginTop = pageH - stepOffsetTop[i]; | ||
const marginBottom = offsetMargin - viewH - stepOffsetHeight[i]; | ||
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`; | ||
const options = { | ||
root: null, | ||
rootMargin, | ||
threshold: 0 | ||
}; | ||
const obs = new IntersectionObserver(intersectStepAbove, options); | ||
const options = { rootMargin }; | ||
// console.log(options); | ||
const obs = new IntersectionObserver(intersectViewportAbove, options); | ||
obs.observe(el); | ||
@@ -427,18 +358,10 @@ return obs; | ||
// bottom edge | ||
function updateStepBelowIO() { | ||
if (io.stepBelow) io.stepBelow.forEach(d => d.disconnect()); | ||
io.stepBelow = stepEl.map((el, i) => { | ||
const marginTop = -offsetMargin; | ||
const marginBottom = ph - vh + stepOffsetHeight[i] + offsetMargin; | ||
function updateViewportBelowIO() { | ||
io.viewportBelow = stepEl.map((el, i) => { | ||
const marginTop = -offsetMargin - stepOffsetHeight[i]; | ||
const marginBottom = offsetMargin - viewH + stepOffsetHeight[i] + pageH; | ||
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`; | ||
const options = { | ||
root: null, | ||
rootMargin, | ||
threshold: 0 | ||
}; | ||
const obs = new IntersectionObserver(intersectStepBelow, options); | ||
const options = { rootMargin }; | ||
// console.log(options); | ||
const obs = new IntersectionObserver(intersectViewportBelow, options); | ||
obs.observe(el); | ||
@@ -449,16 +372,11 @@ return obs; | ||
// jump into viewport | ||
function updateViewportAboveIO() { | ||
if (io.viewportAbove) io.viewportAbove.forEach(d => d.disconnect()); | ||
io.viewportAbove = stepEl.map((el, i) => { | ||
const marginTop = stepOffsetTop[i]; | ||
const marginBottom = -(vh - offsetMargin + stepOffsetHeight[i]); | ||
// look above for intersection | ||
function updateStepAboveIO() { | ||
io.stepAbove = stepEl.map((el, i) => { | ||
const marginTop = -offsetMargin + stepOffsetHeight[i]; | ||
const marginBottom = offsetMargin - viewH; | ||
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`; | ||
const options = { | ||
root: null, | ||
rootMargin, | ||
threshold: 0 | ||
}; | ||
const obs = new IntersectionObserver(intersectViewportAbove, options); | ||
const options = { rootMargin }; | ||
// console.log(options); | ||
const obs = new IntersectionObserver(intersectStepAbove, options); | ||
obs.observe(el); | ||
@@ -469,16 +387,11 @@ return obs; | ||
function updateViewportBelowIO() { | ||
if (io.viewportBelow) io.viewportBelow.forEach(d => d.disconnect()); | ||
io.viewportBelow = stepEl.map((el, i) => { | ||
const marginTop = -(offsetMargin + stepOffsetHeight[i]); | ||
const marginBottom = | ||
ph - stepOffsetTop[i] - stepOffsetHeight[i] - offsetMargin; | ||
// look below for intersection | ||
function updateStepBelowIO() { | ||
io.stepAbove = stepEl.map((el, i) => { | ||
const marginTop = -offsetMargin; | ||
const marginBottom = offsetMargin - viewH + stepOffsetHeight[i]; | ||
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`; | ||
const options = { | ||
root: null, | ||
rootMargin, | ||
threshold: 0 | ||
}; | ||
const obs = new IntersectionObserver(intersectViewportBelow, options); | ||
const options = { rootMargin }; | ||
// console.log(options); | ||
const obs = new IntersectionObserver(intersectStepBelow, options); | ||
obs.observe(el); | ||
@@ -491,16 +404,9 @@ return obs; | ||
function updateStepProgressIO() { | ||
if (io.stepProgress) io.stepProgress.forEach(d => d.disconnect()); | ||
io.stepProgress = stepEl.map((el, i) => { | ||
const marginTop = stepOffsetHeight[i] - offsetMargin; | ||
const marginBottom = -vh + offsetMargin; | ||
const marginBottom = -viewH + offsetMargin; | ||
const rootMargin = `${marginTop}px 0px ${marginBottom}px 0px`; | ||
const threshold = createThreshold(stepOffsetHeight[i]); | ||
const options = { | ||
root: null, | ||
rootMargin, | ||
threshold | ||
}; | ||
const options = { rootMargin, threshold }; | ||
// console.log(options); | ||
const obs = new IntersectionObserver(intersectStepProgress, options); | ||
@@ -513,2 +419,4 @@ obs.observe(el); | ||
function updateIO() { | ||
OBSERVER_NAMES.forEach(disconnectObserver); | ||
updateViewportAboveIO(); | ||
@@ -520,10 +428,5 @@ updateViewportBelowIO(); | ||
if (progressMode) updateStepProgressIO(); | ||
if (containerEl && graphicEl) { | ||
updateTopIO(); | ||
updateBottomIO(); | ||
} | ||
} | ||
// SETUP FUNCTIONS | ||
/*** SETUP FUNCTIONS ***/ | ||
@@ -537,10 +440,9 @@ function indexSteps() { | ||
direction: null, | ||
state: null | ||
state: null, | ||
progress: 0 | ||
})); | ||
containerState = { direction: null, state: null }; | ||
} | ||
function addDebug() { | ||
if (debugMode) bug.setup({ id, stepEl, offsetVal }); | ||
if (isDebug) bug.setup({ id, stepEl, offsetVal }); | ||
} | ||
@@ -551,4 +453,2 @@ | ||
S.setup = ({ | ||
container, | ||
graphic, | ||
step, | ||
@@ -562,9 +462,7 @@ offset = 0.5, | ||
}) => { | ||
id = generateId(); | ||
// elements | ||
// create id unique to this scrollama instance | ||
id = generateInstanceID(); | ||
stepEl = selectAll(step); | ||
containerEl = container ? select(container) : null; | ||
graphicEl = graphic ? select(graphic) : null; | ||
// error if no step selected | ||
if (!stepEl.length) { | ||
@@ -576,3 +474,3 @@ console.error('scrollama error: no step elements'); | ||
// options | ||
debugMode = debug; | ||
isDebug = debug; | ||
progressMode = progress; | ||
@@ -592,3 +490,3 @@ preserveOrder = order; | ||
handleResize(); | ||
handleEnable(true); | ||
S.enable(); | ||
return S; | ||
@@ -614,3 +512,3 @@ }; | ||
handleEnable(false); | ||
Object.keys(callback).forEach(c => (callback[c] = null)); | ||
Object.keys(cb).forEach(c => (cb[c] = null)); | ||
Object.keys(io).forEach(i => (io[i] = null)); | ||
@@ -627,27 +525,20 @@ }; | ||
S.onStepEnter = cb => { | ||
callback.stepEnter = cb; | ||
S.onStepEnter = f => { | ||
if (typeof f === 'function') cb.stepEnter = f; | ||
else console.error('scrollama error: onStepEnter requires a function'); | ||
return S; | ||
}; | ||
S.onStepExit = cb => { | ||
callback.stepExit = cb; | ||
S.onStepExit = f => { | ||
if (typeof f === 'function') cb.stepExit = f; | ||
else console.error('scrollama error: onStepExit requires a function'); | ||
return S; | ||
}; | ||
S.onStepProgress = cb => { | ||
callback.stepProgress = cb; | ||
S.onStepProgress = f => { | ||
if (typeof f === 'function') cb.stepProgress = f; | ||
else console.error('scrollama error: onStepProgress requires a function'); | ||
return S; | ||
}; | ||
S.onContainerEnter = cb => { | ||
callback.containerEnter = cb; | ||
return S; | ||
}; | ||
S.onContainerExit = cb => { | ||
callback.containerExit = cb; | ||
return S; | ||
}; | ||
return S; | ||
@@ -654,0 +545,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
361768
26
1288
221