UNPKG

89.3 kBJavaScriptView Raw
1/*!
2 * ScrollTrigger 3.10.3
3 * https://greensock.com
4 *
5 * @license Copyright 2008-2022, GreenSock. All rights reserved.
6 * Subject to the terms at https://greensock.com/standard-license or for
7 * Club GreenSock members, the agreement issued with that membership.
8 * @author: Jack Doyle, jack@greensock.com
9*/
10
11/* eslint-disable */
12import { Observer, _getTarget, _vertical, _horizontal, _scrollers, _proxies, _getScrollFunc, _getProxyProp, _getVelocityProp } from "./Observer.js";
13
14var gsap,
15 _coreInitted,
16 _win,
17 _doc,
18 _docEl,
19 _body,
20 _root,
21 _resizeDelay,
22 _toArray,
23 _clamp,
24 _time2,
25 _syncInterval,
26 _refreshing,
27 _pointerIsDown,
28 _transformProp,
29 _i,
30 _prevWidth,
31 _prevHeight,
32 _autoRefresh,
33 _sort,
34 _suppressOverwrites,
35 _ignoreResize,
36 _normalizer,
37 _ignoreMobileResize,
38 _baseScreenHeight,
39 _baseScreenWidth,
40 _limitCallbacks,
41 // if true, we'll only trigger callbacks if the active state toggles, so if you scroll immediately past both the start and end positions of a ScrollTrigger (thus inactive to inactive), neither its onEnter nor onLeave will be called. This is useful during startup.
42_startup = 1,
43 _getTime = Date.now,
44 _time1 = _getTime(),
45 _lastScrollTime = 0,
46 _enabled = 0,
47 _pointerDownHandler = function _pointerDownHandler() {
48 return _pointerIsDown = 1;
49},
50 _pointerUpHandler = function _pointerUpHandler() {
51 return _pointerIsDown = 0;
52},
53 _passThrough = function _passThrough(v) {
54 return v;
55},
56 _round = function _round(value) {
57 return Math.round(value * 100000) / 100000 || 0;
58},
59 _windowExists = function _windowExists() {
60 return typeof window !== "undefined";
61},
62 _getGSAP = function _getGSAP() {
63 return gsap || _windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap;
64},
65 _isViewport = function _isViewport(e) {
66 return !!~_root.indexOf(e);
67},
68 _getBoundsFunc = function _getBoundsFunc(element) {
69 return _getProxyProp(element, "getBoundingClientRect") || (_isViewport(element) ? function () {
70 _winOffsets.width = _win.innerWidth;
71 _winOffsets.height = _win.innerHeight;
72 return _winOffsets;
73 } : function () {
74 return _getBounds(element);
75 });
76},
77 _getSizeFunc = function _getSizeFunc(scroller, isViewport, _ref) {
78 var d = _ref.d,
79 d2 = _ref.d2,
80 a = _ref.a;
81 return (a = _getProxyProp(scroller, "getBoundingClientRect")) ? function () {
82 return a()[d];
83 } : function () {
84 return (isViewport ? _win["inner" + d2] : scroller["client" + d2]) || 0;
85 };
86},
87 _getOffsetsFunc = function _getOffsetsFunc(element, isViewport) {
88 return !isViewport || ~_proxies.indexOf(element) ? _getBoundsFunc(element) : function () {
89 return _winOffsets;
90 };
91},
92 _maxScroll = function _maxScroll(element, _ref2) {
93 var s = _ref2.s,
94 d2 = _ref2.d2,
95 d = _ref2.d,
96 a = _ref2.a;
97 return (s = "scroll" + d2) && (a = _getProxyProp(element, s)) ? a() - _getBoundsFunc(element)()[d] : _isViewport(element) ? (_docEl[s] || _body[s]) - (_win["inner" + d2] || _docEl["client" + d2] || _body["client" + d2]) : element[s] - element["offset" + d2];
98},
99 _iterateAutoRefresh = function _iterateAutoRefresh(func, events) {
100 for (var i = 0; i < _autoRefresh.length; i += 3) {
101 (!events || ~events.indexOf(_autoRefresh[i + 1])) && func(_autoRefresh[i], _autoRefresh[i + 1], _autoRefresh[i + 2]);
102 }
103},
104 _isString = function _isString(value) {
105 return typeof value === "string";
106},
107 _isFunction = function _isFunction(value) {
108 return typeof value === "function";
109},
110 _isNumber = function _isNumber(value) {
111 return typeof value === "number";
112},
113 _isObject = function _isObject(value) {
114 return typeof value === "object";
115},
116 _callIfFunc = function _callIfFunc(value) {
117 return _isFunction(value) && value();
118},
119 _combineFunc = function _combineFunc(f1, f2) {
120 return function () {
121 var result1 = _callIfFunc(f1),
122 result2 = _callIfFunc(f2);
123
124 return function () {
125 _callIfFunc(result1);
126
127 _callIfFunc(result2);
128 };
129 };
130},
131 _endAnimation = function _endAnimation(animation, reversed, pause) {
132 return animation && animation.progress(reversed ? 0 : 1) && pause && animation.pause();
133},
134 _callback = function _callback(self, func) {
135 if (self.enabled) {
136 var result = func(self);
137 result && result.totalTime && (self.callbackAnimation = result);
138 }
139},
140 _abs = Math.abs,
141 _scrollLeft = "scrollLeft",
142 _scrollTop = "scrollTop",
143 _left = "left",
144 _top = "top",
145 _right = "right",
146 _bottom = "bottom",
147 _width = "width",
148 _height = "height",
149 _Right = "Right",
150 _Left = "Left",
151 _Top = "Top",
152 _Bottom = "Bottom",
153 _padding = "padding",
154 _margin = "margin",
155 _Width = "Width",
156 _Height = "Height",
157 _px = "px",
158 _getComputedStyle = function _getComputedStyle(element) {
159 return _win.getComputedStyle(element);
160},
161 _makePositionable = function _makePositionable(element) {
162 // if the element already has position: absolute or fixed, leave that, otherwise make it position: relative
163 var position = _getComputedStyle(element).position;
164
165 element.style.position = position === "absolute" || position === "fixed" ? position : "relative";
166},
167 _setDefaults = function _setDefaults(obj, defaults) {
168 for (var p in defaults) {
169 p in obj || (obj[p] = defaults[p]);
170 }
171
172 return obj;
173},
174 _getBounds = function _getBounds(element, withoutTransforms) {
175 var tween = withoutTransforms && _getComputedStyle(element)[_transformProp] !== "matrix(1, 0, 0, 1, 0, 0)" && gsap.to(element, {
176 x: 0,
177 y: 0,
178 xPercent: 0,
179 yPercent: 0,
180 rotation: 0,
181 rotationX: 0,
182 rotationY: 0,
183 scale: 1,
184 skewX: 0,
185 skewY: 0
186 }).progress(1),
187 bounds = element.getBoundingClientRect();
188 tween && tween.progress(0).kill();
189 return bounds;
190},
191 _getSize = function _getSize(element, _ref3) {
192 var d2 = _ref3.d2;
193 return element["offset" + d2] || element["client" + d2] || 0;
194},
195 _getLabelRatioArray = function _getLabelRatioArray(timeline) {
196 var a = [],
197 labels = timeline.labels,
198 duration = timeline.duration(),
199 p;
200
201 for (p in labels) {
202 a.push(labels[p] / duration);
203 }
204
205 return a;
206},
207 _getClosestLabel = function _getClosestLabel(animation) {
208 return function (value) {
209 return gsap.utils.snap(_getLabelRatioArray(animation), value);
210 };
211},
212 _snapDirectional = function _snapDirectional(snapIncrementOrArray) {
213 var snap = gsap.utils.snap(snapIncrementOrArray),
214 a = Array.isArray(snapIncrementOrArray) && snapIncrementOrArray.slice(0).sort(function (a, b) {
215 return a - b;
216 });
217 return a ? function (value, direction, threshold) {
218 if (threshold === void 0) {
219 threshold = 1e-3;
220 }
221
222 var i;
223
224 if (!direction) {
225 return snap(value);
226 }
227
228 if (direction > 0) {
229 value -= threshold; // to avoid rounding errors. If we're too strict, it might snap forward, then immediately again, and again.
230
231 for (i = 0; i < a.length; i++) {
232 if (a[i] >= value) {
233 return a[i];
234 }
235 }
236
237 return a[i - 1];
238 } else {
239 i = a.length;
240 value += threshold;
241
242 while (i--) {
243 if (a[i] <= value) {
244 return a[i];
245 }
246 }
247 }
248
249 return a[0];
250 } : function (value, direction, threshold) {
251 if (threshold === void 0) {
252 threshold = 1e-3;
253 }
254
255 var snapped = snap(value);
256 return !direction || Math.abs(snapped - value) < threshold || snapped - value < 0 === direction < 0 ? snapped : snap(direction < 0 ? value - snapIncrementOrArray : value + snapIncrementOrArray);
257 };
258},
259 _getLabelAtDirection = function _getLabelAtDirection(timeline) {
260 return function (value, st) {
261 return _snapDirectional(_getLabelRatioArray(timeline))(value, st.direction);
262 };
263},
264 _multiListener = function _multiListener(func, element, types, callback) {
265 return types.split(",").forEach(function (type) {
266 return func(element, type, callback);
267 });
268},
269 _addListener = function _addListener(element, type, func, nonPassive, capture) {
270 return element.addEventListener(type, func, {
271 passive: !nonPassive,
272 capture: !!capture
273 });
274},
275 _removeListener = function _removeListener(element, type, func) {
276 return element.removeEventListener(type, func);
277},
278 _wheelListener = function _wheelListener(func, el, scrollFunc) {
279 return scrollFunc && scrollFunc.wheelHandler && func(el, "wheel", scrollFunc);
280},
281 _markerDefaults = {
282 startColor: "green",
283 endColor: "red",
284 indent: 0,
285 fontSize: "16px",
286 fontWeight: "normal"
287},
288 _defaults = {
289 toggleActions: "play",
290 anticipatePin: 0
291},
292 _keywords = {
293 top: 0,
294 left: 0,
295 center: 0.5,
296 bottom: 1,
297 right: 1
298},
299 _offsetToPx = function _offsetToPx(value, size) {
300 if (_isString(value)) {
301 var eqIndex = value.indexOf("="),
302 relative = ~eqIndex ? +(value.charAt(eqIndex - 1) + 1) * parseFloat(value.substr(eqIndex + 1)) : 0;
303
304 if (~eqIndex) {
305 value.indexOf("%") > eqIndex && (relative *= size / 100);
306 value = value.substr(0, eqIndex - 1);
307 }
308
309 value = relative + (value in _keywords ? _keywords[value] * size : ~value.indexOf("%") ? parseFloat(value) * size / 100 : parseFloat(value) || 0);
310 }
311
312 return value;
313},
314 _createMarker = function _createMarker(type, name, container, direction, _ref4, offset, matchWidthEl, containerAnimation) {
315 var startColor = _ref4.startColor,
316 endColor = _ref4.endColor,
317 fontSize = _ref4.fontSize,
318 indent = _ref4.indent,
319 fontWeight = _ref4.fontWeight;
320
321 var e = _doc.createElement("div"),
322 useFixedPosition = _isViewport(container) || _getProxyProp(container, "pinType") === "fixed",
323 isScroller = type.indexOf("scroller") !== -1,
324 parent = useFixedPosition ? _body : container,
325 isStart = type.indexOf("start") !== -1,
326 color = isStart ? startColor : endColor,
327 css = "border-color:" + color + ";font-size:" + fontSize + ";color:" + color + ";font-weight:" + fontWeight + ";pointer-events:none;white-space:nowrap;font-family:sans-serif,Arial;z-index:1000;padding:4px 8px;border-width:0;border-style:solid;";
328
329 css += "position:" + ((isScroller || containerAnimation) && useFixedPosition ? "fixed;" : "absolute;");
330 (isScroller || containerAnimation || !useFixedPosition) && (css += (direction === _vertical ? _right : _bottom) + ":" + (offset + parseFloat(indent)) + "px;");
331 matchWidthEl && (css += "box-sizing:border-box;text-align:left;width:" + matchWidthEl.offsetWidth + "px;");
332 e._isStart = isStart;
333 e.setAttribute("class", "gsap-marker-" + type + (name ? " marker-" + name : ""));
334 e.style.cssText = css;
335 e.innerText = name || name === 0 ? type + "-" + name : type;
336 parent.children[0] ? parent.insertBefore(e, parent.children[0]) : parent.appendChild(e);
337 e._offset = e["offset" + direction.op.d2];
338
339 _positionMarker(e, 0, direction, isStart);
340
341 return e;
342},
343 _positionMarker = function _positionMarker(marker, start, direction, flipped) {
344 var vars = {
345 display: "block"
346 },
347 side = direction[flipped ? "os2" : "p2"],
348 oppositeSide = direction[flipped ? "p2" : "os2"];
349 marker._isFlipped = flipped;
350 vars[direction.a + "Percent"] = flipped ? -100 : 0;
351 vars[direction.a] = flipped ? "1px" : 0;
352 vars["border" + side + _Width] = 1;
353 vars["border" + oppositeSide + _Width] = 0;
354 vars[direction.p] = start + "px";
355 gsap.set(marker, vars);
356},
357 _triggers = [],
358 _ids = {},
359 _rafID,
360 _sync = function _sync() {
361 return _getTime() - _lastScrollTime > 34 && _updateAll();
362},
363 _onScroll = function _onScroll() {
364 // previously, we tried to optimize performance by batching/deferring to the next requestAnimationFrame(), but discovered that Safari has a few bugs that make this unworkable (especially on iOS). See https://codepen.io/GreenSock/pen/16c435b12ef09c38125204818e7b45fc?editors=0010 and https://codepen.io/GreenSock/pen/JjOxYpQ/3dd65ccec5a60f1d862c355d84d14562?editors=0010 and https://codepen.io/GreenSock/pen/ExbrPNa/087cef197dc35445a0951e8935c41503?editors=0010
365 if (!_normalizer || !_normalizer.isPressed) {
366 _scrollers.cache++;
367 _rafID || (_rafID = requestAnimationFrame(_updateAll));
368 _lastScrollTime || _dispatch("scrollStart");
369 _lastScrollTime = _getTime();
370 }
371},
372 _setBaseDimensions = function _setBaseDimensions() {
373 _baseScreenWidth = _win.innerWidth;
374 _baseScreenHeight = _win.innerHeight;
375},
376 _onResize = function _onResize() {
377 _scrollers.cache++;
378 !_refreshing && !_ignoreResize && !_doc.fullscreenElement && (!_ignoreMobileResize || _baseScreenWidth !== _win.innerWidth || Math.abs(_win.innerHeight - _baseScreenHeight) > _win.innerHeight * 0.25) && _resizeDelay.restart(true);
379},
380 // ignore resizes triggered by refresh()
381_listeners = {},
382 _emptyArray = [],
383 _media = [],
384 _creatingMedia,
385 // when ScrollTrigger.matchMedia() is called, we record the current media key here (like "(min-width: 800px)") so that we can assign it to everything that's created during that call. Then we can revert just those when necessary. In the ScrollTrigger's init() call, the _creatingMedia is recorded as a "media" property on the instance.
386_lastMediaTick,
387 _onMediaChange = function _onMediaChange(e) {
388 var tick = gsap.ticker.frame,
389 matches = [],
390 i = 0,
391 index;
392
393 if (_lastMediaTick !== tick || _startup) {
394 _revertAll();
395
396 for (; i < _media.length; i += 4) {
397 index = _win.matchMedia(_media[i]).matches;
398
399 if (index !== _media[i + 3]) {
400 // note: some browsers fire the matchMedia event multiple times, like when going full screen, so we shouldn't call the function multiple times. Check to see if it's already matched.
401 _media[i + 3] = index;
402 index ? matches.push(i) : _revertAll(1, _media[i]) || _isFunction(_media[i + 2]) && _media[i + 2](); // Firefox doesn't update the "matches" property of the MediaQueryList object correctly - it only does so as it calls its change handler - so we must re-create a media query here to ensure it's accurate.
403 }
404 }
405
406 _revertRecorded(); // in case killing/reverting any of the animations actually added inline styles back.
407
408
409 for (i = 0; i < matches.length; i++) {
410 index = matches[i];
411 _creatingMedia = _media[index];
412 _media[index + 2] = _media[index + 1](e);
413 }
414
415 _creatingMedia = 0;
416 _coreInitted && _refreshAll(0, 1);
417 _lastMediaTick = tick;
418
419 _dispatch("matchMedia");
420 }
421},
422 _softRefresh = function _softRefresh() {
423 return _removeListener(ScrollTrigger, "scrollEnd", _softRefresh) || _refreshAll(true);
424},
425 _dispatch = function _dispatch(type) {
426 return _listeners[type] && _listeners[type].map(function (f) {
427 return f();
428 }) || _emptyArray;
429},
430 _savedStyles = [],
431 // when ScrollTrigger.saveStyles() is called, the inline styles are recorded in this Array in a sequential format like [element, cssText, gsCache, media]. This keeps it very memory-efficient and fast to iterate through.
432_revertRecorded = function _revertRecorded(media) {
433 for (var i = 0; i < _savedStyles.length; i += 5) {
434 if (!media || _savedStyles[i + 4] === media) {
435 _savedStyles[i].style.cssText = _savedStyles[i + 1];
436 _savedStyles[i].getBBox && _savedStyles[i].setAttribute("transform", _savedStyles[i + 2] || "");
437 _savedStyles[i + 3].uncache = 1;
438 }
439 }
440},
441 _revertAll = function _revertAll(kill, media) {
442 var trigger;
443
444 for (_i = 0; _i < _triggers.length; _i++) {
445 trigger = _triggers[_i];
446
447 if (!media || trigger.media === media) {
448 if (kill) {
449 trigger.kill(1);
450 } else {
451 trigger.revert();
452 }
453 }
454 }
455
456 media && _revertRecorded(media);
457 media || _dispatch("revert");
458},
459 _clearScrollMemory = function _clearScrollMemory() {
460 return _scrollers.cache++ && _scrollers.forEach(function (obj) {
461 return typeof obj === "function" && (obj.rec = 0);
462 });
463},
464 // zero-out all the recorded scroll positions. Don't use _triggers because if, for example, .matchMedia() is used to create some ScrollTriggers and then the user resizes and it removes ALL ScrollTriggers, and then go back to a size where there are ScrollTriggers, it would have kept the position(s) saved from the initial state.
465_refreshingAll,
466 _refreshID = 0,
467 _refreshAll = function _refreshAll(force, skipRevert) {
468 if (_lastScrollTime && !force) {
469 _addListener(ScrollTrigger, "scrollEnd", _softRefresh);
470
471 return;
472 }
473
474 _refreshingAll = true;
475
476 var refreshInits = _dispatch("refreshInit");
477
478 _sort && ScrollTrigger.sort();
479 skipRevert || _revertAll();
480
481 _triggers.slice(0).forEach(function (t) {
482 return t.refresh();
483 }); // don't loop with _i because during a refresh() someone could call ScrollTrigger.update() which would iterate through _i resulting in a skip.
484
485
486 _triggers.forEach(function (t) {
487 return t.vars.end === "max" && t.setPositions(t.start, _maxScroll(t.scroller, t._dir));
488 }); // the scroller's max scroll position may change after all the ScrollTriggers refreshed (like pinning could push it down), so we need to loop back and correct any with end: "max".
489
490
491 refreshInits.forEach(function (result) {
492 return result && result.render && result.render(-1);
493 }); // if the onRefreshInit() returns an animation (typically a gsap.set()), revert it. This makes it easy to put things in a certain spot before refreshing for measurement purposes, and then put things back.
494
495 _clearScrollMemory();
496
497 _resizeDelay.pause();
498
499 _refreshID++;
500 _refreshingAll = false;
501
502 _dispatch("refresh");
503},
504 _lastScroll = 0,
505 _direction = 1,
506 _primary,
507 _updateAll = function _updateAll() {
508 if (!_refreshingAll) {
509 _primary && _primary.update(0); // ScrollSmoother users refreshPriority -9999 to become the primary that gets updated before all others because it affects the scroll position.
510
511 ScrollTrigger.isUpdating = true;
512
513 var l = _triggers.length,
514 time = _getTime(),
515 recordVelocity = time - _time1 >= 50,
516 scroll = l && _triggers[0].scroll();
517
518 _direction = _lastScroll > scroll ? -1 : 1;
519 _lastScroll = scroll;
520
521 if (recordVelocity) {
522 if (_lastScrollTime && !_pointerIsDown && time - _lastScrollTime > 200) {
523 _lastScrollTime = 0;
524
525 _dispatch("scrollEnd");
526 }
527
528 _time2 = _time1;
529 _time1 = time;
530 }
531
532 if (_direction < 0) {
533 _i = l;
534
535 while (_i-- > 0) {
536 _triggers[_i] && _triggers[_i].update(0, recordVelocity);
537 }
538
539 _direction = 1;
540 } else {
541 for (_i = 0; _i < l; _i++) {
542 _triggers[_i] && _triggers[_i].update(0, recordVelocity);
543 }
544 }
545
546 ScrollTrigger.isUpdating = false;
547 }
548
549 _rafID = 0;
550},
551 _propNamesToCopy = [_left, _top, _bottom, _right, _margin + _Bottom, _margin + _Right, _margin + _Top, _margin + _Left, "display", "flexShrink", "float", "zIndex", "gridColumnStart", "gridColumnEnd", "gridRowStart", "gridRowEnd", "gridArea", "justifySelf", "alignSelf", "placeSelf", "order"],
552 _stateProps = _propNamesToCopy.concat([_width, _height, "boxSizing", "max" + _Width, "max" + _Height, "position", _margin, _padding, _padding + _Top, _padding + _Right, _padding + _Bottom, _padding + _Left]),
553 _swapPinOut = function _swapPinOut(pin, spacer, state) {
554 _setState(state);
555
556 var cache = pin._gsap;
557
558 if (cache.spacerIsNative) {
559 _setState(cache.spacerState);
560 } else if (pin.parentNode === spacer) {
561 var parent = spacer.parentNode;
562
563 if (parent) {
564 parent.insertBefore(pin, spacer);
565 parent.removeChild(spacer);
566 }
567 }
568},
569 _swapPinIn = function _swapPinIn(pin, spacer, cs, spacerState) {
570 if (pin.parentNode !== spacer) {
571 var i = _propNamesToCopy.length,
572 spacerStyle = spacer.style,
573 pinStyle = pin.style,
574 p;
575
576 while (i--) {
577 p = _propNamesToCopy[i];
578 spacerStyle[p] = cs[p];
579 }
580
581 spacerStyle.position = cs.position === "absolute" ? "absolute" : "relative";
582 cs.display === "inline" && (spacerStyle.display = "inline-block");
583 pinStyle[_bottom] = pinStyle[_right] = spacerStyle.flexBasis = "auto";
584 spacerStyle.overflow = "visible";
585 spacerStyle.boxSizing = "border-box";
586 spacerStyle[_width] = _getSize(pin, _horizontal) + _px;
587 spacerStyle[_height] = _getSize(pin, _vertical) + _px;
588 spacerStyle[_padding] = pinStyle[_margin] = pinStyle[_top] = pinStyle[_left] = "0";
589
590 _setState(spacerState);
591
592 pinStyle[_width] = pinStyle["max" + _Width] = cs[_width];
593 pinStyle[_height] = pinStyle["max" + _Height] = cs[_height];
594 pinStyle[_padding] = cs[_padding];
595 pin.parentNode.insertBefore(spacer, pin);
596 spacer.appendChild(pin);
597 }
598},
599 _capsExp = /([A-Z])/g,
600 _setState = function _setState(state) {
601 if (state) {
602 var style = state.t.style,
603 l = state.length,
604 i = 0,
605 p,
606 value;
607 (state.t._gsap || gsap.core.getCache(state.t)).uncache = 1; // otherwise transforms may be off
608
609 for (; i < l; i += 2) {
610 value = state[i + 1];
611 p = state[i];
612
613 if (value) {
614 style[p] = value;
615 } else if (style[p]) {
616 style.removeProperty(p.replace(_capsExp, "-$1").toLowerCase());
617 }
618 }
619 }
620},
621 _getState = function _getState(element) {
622 // returns an Array with alternating values like [property, value, property, value] and a "t" property pointing to the target (element). Makes it fast and cheap.
623 var l = _stateProps.length,
624 style = element.style,
625 state = [],
626 i = 0;
627
628 for (; i < l; i++) {
629 state.push(_stateProps[i], style[_stateProps[i]]);
630 }
631
632 state.t = element;
633 return state;
634},
635 _copyState = function _copyState(state, override, omitOffsets) {
636 var result = [],
637 l = state.length,
638 i = omitOffsets ? 8 : 0,
639 // skip top, left, right, bottom if omitOffsets is true
640 p;
641
642 for (; i < l; i += 2) {
643 p = state[i];
644 result.push(p, p in override ? override[p] : state[i + 1]);
645 }
646
647 result.t = state.t;
648 return result;
649},
650 _winOffsets = {
651 left: 0,
652 top: 0
653},
654 // // potential future feature (?) Allow users to calculate where a trigger hits (scroll position) like getScrollPosition("#id", "top bottom")
655// _getScrollPosition = (trigger, position, {scroller, containerAnimation, horizontal}) => {
656// scroller = _getTarget(scroller || _win);
657// let direction = horizontal ? _horizontal : _vertical,
658// isViewport = _isViewport(scroller);
659// _getSizeFunc(scroller, isViewport, direction);
660// return _parsePosition(position, _getTarget(trigger), _getSizeFunc(scroller, isViewport, direction)(), direction, _getScrollFunc(scroller, direction)(), 0, 0, 0, _getOffsetsFunc(scroller, isViewport)(), isViewport ? 0 : parseFloat(_getComputedStyle(scroller)["border" + direction.p2 + _Width]) || 0, 0, containerAnimation ? containerAnimation.duration() : _maxScroll(scroller), containerAnimation);
661// },
662_parsePosition = function _parsePosition(value, trigger, scrollerSize, direction, scroll, marker, markerScroller, self, scrollerBounds, borderWidth, useFixedPosition, scrollerMax, containerAnimation) {
663 _isFunction(value) && (value = value(self));
664
665 if (_isString(value) && value.substr(0, 3) === "max") {
666 value = scrollerMax + (value.charAt(4) === "=" ? _offsetToPx("0" + value.substr(3), scrollerSize) : 0);
667 }
668
669 var time = containerAnimation ? containerAnimation.time() : 0,
670 p1,
671 p2,
672 element;
673 containerAnimation && containerAnimation.seek(0);
674
675 if (!_isNumber(value)) {
676 _isFunction(trigger) && (trigger = trigger(self));
677 var offsets = value.split(" "),
678 bounds,
679 localOffset,
680 globalOffset,
681 display;
682 element = _getTarget(trigger) || _body;
683 bounds = _getBounds(element) || {};
684
685 if ((!bounds || !bounds.left && !bounds.top) && _getComputedStyle(element).display === "none") {
686 // if display is "none", it won't report getBoundingClientRect() properly
687 display = element.style.display;
688 element.style.display = "block";
689 bounds = _getBounds(element);
690 display ? element.style.display = display : element.style.removeProperty("display");
691 }
692
693 localOffset = _offsetToPx(offsets[0], bounds[direction.d]);
694 globalOffset = _offsetToPx(offsets[1] || "0", scrollerSize);
695 value = bounds[direction.p] - scrollerBounds[direction.p] - borderWidth + localOffset + scroll - globalOffset;
696 markerScroller && _positionMarker(markerScroller, globalOffset, direction, scrollerSize - globalOffset < 20 || markerScroller._isStart && globalOffset > 20);
697 scrollerSize -= scrollerSize - globalOffset; // adjust for the marker
698 } else if (markerScroller) {
699 _positionMarker(markerScroller, scrollerSize, direction, true);
700 }
701
702 if (marker) {
703 var position = value + scrollerSize,
704 isStart = marker._isStart;
705 p1 = "scroll" + direction.d2;
706
707 _positionMarker(marker, position, direction, isStart && position > 20 || !isStart && (useFixedPosition ? Math.max(_body[p1], _docEl[p1]) : marker.parentNode[p1]) <= position + 1);
708
709 if (useFixedPosition) {
710 scrollerBounds = _getBounds(markerScroller);
711 useFixedPosition && (marker.style[direction.op.p] = scrollerBounds[direction.op.p] - direction.op.m - marker._offset + _px);
712 }
713 }
714
715 if (containerAnimation && element) {
716 p1 = _getBounds(element);
717 containerAnimation.seek(scrollerMax);
718 p2 = _getBounds(element);
719 containerAnimation._caScrollDist = p1[direction.p] - p2[direction.p];
720 value = value / containerAnimation._caScrollDist * scrollerMax;
721 }
722
723 containerAnimation && containerAnimation.seek(time);
724 return containerAnimation ? value : Math.round(value);
725},
726 _prefixExp = /(webkit|moz|length|cssText|inset)/i,
727 _reparent = function _reparent(element, parent, top, left) {
728 if (element.parentNode !== parent) {
729 var style = element.style,
730 p,
731 cs;
732
733 if (parent === _body) {
734 element._stOrig = style.cssText; // record original inline styles so we can revert them later
735
736 cs = _getComputedStyle(element);
737
738 for (p in cs) {
739 // must copy all relevant styles to ensure that nothing changes visually when we reparent to the <body>. Skip the vendor prefixed ones.
740 if (!+p && !_prefixExp.test(p) && cs[p] && typeof style[p] === "string" && p !== "0") {
741 style[p] = cs[p];
742 }
743 }
744
745 style.top = top;
746 style.left = left;
747 } else {
748 style.cssText = element._stOrig;
749 }
750
751 gsap.core.getCache(element).uncache = 1;
752 parent.appendChild(element);
753 }
754},
755 // _mergeAnimations = animations => {
756// let tl = gsap.timeline({smoothChildTiming: true}).startTime(Math.min(...animations.map(a => a.globalTime(0))));
757// animations.forEach(a => {let time = a.totalTime(); tl.add(a); a.totalTime(time); });
758// tl.smoothChildTiming = false;
759// return tl;
760// },
761// returns a function that can be used to tween the scroll position in the direction provided, and when doing so it'll add a .tween property to the FUNCTION itself, and remove it when the tween completes or gets killed. This gives us a way to have multiple ScrollTriggers use a central function for any given scroller and see if there's a scroll tween running (which would affect if/how things get updated)
762_getTweenCreator = function _getTweenCreator(scroller, direction) {
763 var getScroll = _getScrollFunc(scroller, direction),
764 prop = "_scroll" + direction.p2,
765 // add a tweenable property to the scroller that's a getter/setter function, like _scrollTop or _scrollLeft. This way, if someone does gsap.killTweensOf(scroller) it'll kill the scroll tween.
766 lastScroll1,
767 lastScroll2,
768 getTween = function getTween(scrollTo, vars, initialValue, change1, change2) {
769 var tween = getTween.tween,
770 onComplete = vars.onComplete,
771 modifiers = {};
772 initialValue = initialValue || getScroll();
773 change2 = change1 && change2 || 0; // if change1 is 0, we set that to the difference and ignore change2. Otherwise, there would be a compound effect.
774
775 change1 = change1 || scrollTo - initialValue;
776 tween && tween.kill();
777 lastScroll1 = Math.round(initialValue);
778 vars[prop] = scrollTo;
779 vars.modifiers = modifiers;
780
781 modifiers[prop] = function (value) {
782 value = _round(getScroll()); // round because in some [very uncommon] Windows environments, it can get reported with decimals even though it was set without.
783
784 if (value !== lastScroll1 && value !== lastScroll2 && Math.abs(value - lastScroll1) > 2 && Math.abs(value - lastScroll2) > 2) {
785 // if the user scrolls, kill the tween. iOS Safari intermittently misreports the scroll position, it may be the most recently-set one or the one before that! When Safari is zoomed (CMD-+), it often misreports as 1 pixel off too! So if we set the scroll position to 125, for example, it'll actually report it as 124.
786 tween.kill();
787 getTween.tween = 0;
788 } else {
789 value = initialValue + change1 * tween.ratio + change2 * tween.ratio * tween.ratio;
790 }
791
792 lastScroll2 = lastScroll1;
793 return lastScroll1 = _round(value);
794 };
795
796 vars.onComplete = function () {
797 getTween.tween = 0;
798 onComplete && onComplete.call(tween);
799 };
800
801 tween = getTween.tween = gsap.to(scroller, vars);
802 return tween;
803 };
804
805 scroller[prop] = getScroll;
806
807 getScroll.wheelHandler = function () {
808 return getTween.tween && getTween.tween.kill() && (getTween.tween = 0);
809 };
810
811 _addListener(scroller, "wheel", getScroll.wheelHandler); // Windows machines handle mousewheel scrolling in chunks (like "3 lines per scroll") meaning the typical strategy for cancelling the scroll isn't as sensitive. It's much more likely to match one of the previous 2 scroll event positions. So we kill any snapping as soon as there's a wheel event.
812
813
814 return getTween;
815};
816
817export var ScrollTrigger = /*#__PURE__*/function () {
818 function ScrollTrigger(vars, animation) {
819 _coreInitted || ScrollTrigger.register(gsap) || console.warn("Please gsap.registerPlugin(ScrollTrigger)");
820 this.init(vars, animation);
821 }
822
823 var _proto = ScrollTrigger.prototype;
824
825 _proto.init = function init(vars, animation) {
826 this.progress = this.start = 0;
827 this.vars && this.kill(true, true); // in case it's being initted again
828
829 if (!_enabled) {
830 this.update = this.refresh = this.kill = _passThrough;
831 return;
832 }
833
834 vars = _setDefaults(_isString(vars) || _isNumber(vars) || vars.nodeType ? {
835 trigger: vars
836 } : vars, _defaults);
837
838 var _vars = vars,
839 onUpdate = _vars.onUpdate,
840 toggleClass = _vars.toggleClass,
841 id = _vars.id,
842 onToggle = _vars.onToggle,
843 onRefresh = _vars.onRefresh,
844 scrub = _vars.scrub,
845 trigger = _vars.trigger,
846 pin = _vars.pin,
847 pinSpacing = _vars.pinSpacing,
848 invalidateOnRefresh = _vars.invalidateOnRefresh,
849 anticipatePin = _vars.anticipatePin,
850 onScrubComplete = _vars.onScrubComplete,
851 onSnapComplete = _vars.onSnapComplete,
852 once = _vars.once,
853 snap = _vars.snap,
854 pinReparent = _vars.pinReparent,
855 pinSpacer = _vars.pinSpacer,
856 containerAnimation = _vars.containerAnimation,
857 fastScrollEnd = _vars.fastScrollEnd,
858 preventOverlaps = _vars.preventOverlaps,
859 direction = vars.horizontal || vars.containerAnimation && vars.horizontal !== false ? _horizontal : _vertical,
860 isToggle = !scrub && scrub !== 0,
861 scroller = _getTarget(vars.scroller || _win),
862 scrollerCache = gsap.core.getCache(scroller),
863 isViewport = _isViewport(scroller),
864 useFixedPosition = ("pinType" in vars ? vars.pinType : _getProxyProp(scroller, "pinType") || isViewport && "fixed") === "fixed",
865 callbacks = [vars.onEnter, vars.onLeave, vars.onEnterBack, vars.onLeaveBack],
866 toggleActions = isToggle && vars.toggleActions.split(" "),
867 markers = "markers" in vars ? vars.markers : _defaults.markers,
868 borderWidth = isViewport ? 0 : parseFloat(_getComputedStyle(scroller)["border" + direction.p2 + _Width]) || 0,
869 self = this,
870 onRefreshInit = vars.onRefreshInit && function () {
871 return vars.onRefreshInit(self);
872 },
873 getScrollerSize = _getSizeFunc(scroller, isViewport, direction),
874 getScrollerOffsets = _getOffsetsFunc(scroller, isViewport),
875 lastSnap = 0,
876 lastRefresh = 0,
877 scrollFunc = _getScrollFunc(scroller, direction),
878 tweenTo,
879 pinCache,
880 snapFunc,
881 scroll1,
882 scroll2,
883 start,
884 end,
885 markerStart,
886 markerEnd,
887 markerStartTrigger,
888 markerEndTrigger,
889 markerVars,
890 change,
891 pinOriginalState,
892 pinActiveState,
893 pinState,
894 spacer,
895 offset,
896 pinGetter,
897 pinSetter,
898 pinStart,
899 pinChange,
900 spacingStart,
901 spacerState,
902 markerStartSetter,
903 markerEndSetter,
904 cs,
905 snap1,
906 snap2,
907 scrubTween,
908 scrubSmooth,
909 snapDurClamp,
910 snapDelayedCall,
911 prevProgress,
912 prevScroll,
913 prevAnimProgress,
914 caMarkerSetter,
915 customRevertReturn;
916
917 self.media = _creatingMedia;
918 self._dir = direction;
919 anticipatePin *= 45;
920 self.scroller = scroller;
921 self.scroll = containerAnimation ? containerAnimation.time.bind(containerAnimation) : scrollFunc;
922 scroll1 = scrollFunc();
923 self.vars = vars;
924 animation = animation || vars.animation;
925
926 if ("refreshPriority" in vars) {
927 _sort = 1;
928 vars.refreshPriority === -9999 && (_primary = self); // used by ScrollSmoother
929 }
930
931 scrollerCache.tweenScroll = scrollerCache.tweenScroll || {
932 top: _getTweenCreator(scroller, _vertical),
933 left: _getTweenCreator(scroller, _horizontal)
934 };
935 self.tweenTo = tweenTo = scrollerCache.tweenScroll[direction.p];
936
937 self.scrubDuration = function (value) {
938 scrubSmooth = _isNumber(value) && value;
939
940 if (!scrubSmooth) {
941 scrubTween && scrubTween.progress(1).kill();
942 scrubTween = 0;
943 } else {
944 scrubTween ? scrubTween.duration(value) : scrubTween = gsap.to(animation, {
945 ease: "expo",
946 totalProgress: "+=0.001",
947 duration: scrubSmooth,
948 paused: true,
949 onComplete: function onComplete() {
950 return onScrubComplete && onScrubComplete(self);
951 }
952 });
953 }
954 };
955
956 if (animation) {
957 animation.vars.lazy = false;
958 animation._initted || animation.vars.immediateRender !== false && vars.immediateRender !== false && animation.render(0, true, true);
959 self.animation = animation.pause();
960 animation.scrollTrigger = self;
961 self.scrubDuration(scrub);
962 snap1 = 0;
963 id || (id = animation.vars.id);
964 }
965
966 _triggers.push(self);
967
968 if (snap) {
969 if (!_isObject(snap) || snap.push) {
970 snap = {
971 snapTo: snap
972 };
973 }
974
975 "scrollBehavior" in _body.style && gsap.set(isViewport ? [_body, _docEl] : scroller, {
976 scrollBehavior: "auto"
977 }); // smooth scrolling doesn't work with snap.
978
979 snapFunc = _isFunction(snap.snapTo) ? snap.snapTo : snap.snapTo === "labels" ? _getClosestLabel(animation) : snap.snapTo === "labelsDirectional" ? _getLabelAtDirection(animation) : snap.directional !== false ? function (value, st) {
980 return _snapDirectional(snap.snapTo)(value, _getTime() - lastRefresh < 500 ? 0 : st.direction);
981 } : gsap.utils.snap(snap.snapTo);
982 snapDurClamp = snap.duration || {
983 min: 0.1,
984 max: 2
985 };
986 snapDurClamp = _isObject(snapDurClamp) ? _clamp(snapDurClamp.min, snapDurClamp.max) : _clamp(snapDurClamp, snapDurClamp);
987 snapDelayedCall = gsap.delayedCall(snap.delay || scrubSmooth / 2 || 0.1, function () {
988 var scroll = scrollFunc(),
989 refreshedRecently = _getTime() - lastRefresh < 500,
990 tween = tweenTo.tween;
991
992 if ((refreshedRecently || Math.abs(self.getVelocity()) < 10) && !tween && !_pointerIsDown && lastSnap !== scroll) {
993 var progress = (scroll - start) / change,
994 totalProgress = animation && !isToggle ? animation.totalProgress() : progress,
995 velocity = refreshedRecently ? 0 : (totalProgress - snap2) / (_getTime() - _time2) * 1000 || 0,
996 change1 = gsap.utils.clamp(-progress, 1 - progress, _abs(velocity / 2) * velocity / 0.185),
997 naturalEnd = progress + (snap.inertia === false ? 0 : change1),
998 endValue = _clamp(0, 1, snapFunc(naturalEnd, self)),
999 endScroll = Math.round(start + endValue * change),
1000 _snap = snap,
1001 onStart = _snap.onStart,
1002 _onInterrupt = _snap.onInterrupt,
1003 _onComplete = _snap.onComplete;
1004
1005 if (scroll <= end && scroll >= start && endScroll !== scroll) {
1006 if (tween && !tween._initted && tween.data <= _abs(endScroll - scroll)) {
1007 // there's an overlapping snap! So we must figure out which one is closer and let that tween live.
1008 return;
1009 }
1010
1011 if (snap.inertia === false) {
1012 change1 = endValue - progress;
1013 }
1014
1015 tweenTo(endScroll, {
1016 duration: snapDurClamp(_abs(Math.max(_abs(naturalEnd - totalProgress), _abs(endValue - totalProgress)) * 0.185 / velocity / 0.05 || 0)),
1017 ease: snap.ease || "power3",
1018 data: _abs(endScroll - scroll),
1019 // record the distance so that if another snap tween occurs (conflict) we can prioritize the closest snap.
1020 onInterrupt: function onInterrupt() {
1021 return snapDelayedCall.restart(true) && _onInterrupt && _onInterrupt(self);
1022 },
1023 onComplete: function onComplete() {
1024 self.update();
1025 lastSnap = scrollFunc();
1026 snap1 = snap2 = animation && !isToggle ? animation.totalProgress() : self.progress;
1027 onSnapComplete && onSnapComplete(self);
1028 _onComplete && _onComplete(self);
1029 }
1030 }, scroll, change1 * change, endScroll - scroll - change1 * change);
1031 onStart && onStart(self, tweenTo.tween);
1032 }
1033 } else if (self.isActive && lastSnap !== scroll) {
1034 snapDelayedCall.restart(true);
1035 }
1036 }).pause();
1037 }
1038
1039 id && (_ids[id] = self);
1040 trigger = self.trigger = _getTarget(trigger || pin); // if a trigger has some kind of scroll-related effect applied that could contaminate the "y" or "x" position (like a ScrollSmoother effect), we needed a way to temporarily revert it, so we use the stRevert property of the gsCache. It can return another function that we'll call at the end so it can return to its normal state.
1041
1042 customRevertReturn = trigger && trigger._gsap && trigger._gsap.stRevert;
1043 customRevertReturn && (customRevertReturn = customRevertReturn(self));
1044 pin = pin === true ? trigger : _getTarget(pin);
1045 _isString(toggleClass) && (toggleClass = {
1046 targets: trigger,
1047 className: toggleClass
1048 });
1049
1050 if (pin) {
1051 pinSpacing === false || pinSpacing === _margin || (pinSpacing = !pinSpacing && _getComputedStyle(pin.parentNode).display === "flex" ? false : _padding); // if the parent is display: flex, don't apply pinSpacing by default.
1052
1053 self.pin = pin;
1054 vars.force3D !== false && gsap.set(pin, {
1055 force3D: true
1056 });
1057 pinCache = gsap.core.getCache(pin);
1058
1059 if (!pinCache.spacer) {
1060 // record the spacer and pinOriginalState on the cache in case someone tries pinning the same element with MULTIPLE ScrollTriggers - we don't want to have multiple spacers or record the "original" pin state after it has already been affected by another ScrollTrigger.
1061 if (pinSpacer) {
1062 pinSpacer = _getTarget(pinSpacer);
1063 pinSpacer && !pinSpacer.nodeType && (pinSpacer = pinSpacer.current || pinSpacer.nativeElement); // for React & Angular
1064
1065 pinCache.spacerIsNative = !!pinSpacer;
1066 pinSpacer && (pinCache.spacerState = _getState(pinSpacer));
1067 }
1068
1069 pinCache.spacer = spacer = pinSpacer || _doc.createElement("div");
1070 spacer.classList.add("pin-spacer");
1071 id && spacer.classList.add("pin-spacer-" + id);
1072 pinCache.pinState = pinOriginalState = _getState(pin);
1073 } else {
1074 pinOriginalState = pinCache.pinState;
1075 }
1076
1077 self.spacer = spacer = pinCache.spacer;
1078 cs = _getComputedStyle(pin);
1079 spacingStart = cs[pinSpacing + direction.os2];
1080 pinGetter = gsap.getProperty(pin);
1081 pinSetter = gsap.quickSetter(pin, direction.a, _px); // pin.firstChild && !_maxScroll(pin, direction) && (pin.style.overflow = "hidden"); // protects from collapsing margins, but can have unintended consequences as demonstrated here: https://codepen.io/GreenSock/pen/1e42c7a73bfa409d2cf1e184e7a4248d so it was removed in favor of just telling people to set up their CSS to avoid the collapsing margins (overflow: hidden | auto is just one option. Another is border-top: 1px solid transparent).
1082
1083 _swapPinIn(pin, spacer, cs);
1084
1085 pinState = _getState(pin);
1086 }
1087
1088 if (markers) {
1089 markerVars = _isObject(markers) ? _setDefaults(markers, _markerDefaults) : _markerDefaults;
1090 markerStartTrigger = _createMarker("scroller-start", id, scroller, direction, markerVars, 0);
1091 markerEndTrigger = _createMarker("scroller-end", id, scroller, direction, markerVars, 0, markerStartTrigger);
1092 offset = markerStartTrigger["offset" + direction.op.d2];
1093
1094 var content = _getTarget(_getProxyProp(scroller, "content") || scroller);
1095
1096 markerStart = this.markerStart = _createMarker("start", id, content, direction, markerVars, offset, 0, containerAnimation);
1097 markerEnd = this.markerEnd = _createMarker("end", id, content, direction, markerVars, offset, 0, containerAnimation);
1098 containerAnimation && (caMarkerSetter = gsap.quickSetter([markerStart, markerEnd], direction.a, _px));
1099
1100 if (!useFixedPosition && !(_proxies.length && _getProxyProp(scroller, "fixedMarkers") === true)) {
1101 _makePositionable(isViewport ? _body : scroller);
1102
1103 gsap.set([markerStartTrigger, markerEndTrigger], {
1104 force3D: true
1105 });
1106 markerStartSetter = gsap.quickSetter(markerStartTrigger, direction.a, _px);
1107 markerEndSetter = gsap.quickSetter(markerEndTrigger, direction.a, _px);
1108 }
1109 }
1110
1111 if (containerAnimation) {
1112 var oldOnUpdate = containerAnimation.vars.onUpdate,
1113 oldParams = containerAnimation.vars.onUpdateParams;
1114 containerAnimation.eventCallback("onUpdate", function () {
1115 self.update(0, 0, 1);
1116 oldOnUpdate && oldOnUpdate.apply(oldParams || []);
1117 });
1118 }
1119
1120 self.previous = function () {
1121 return _triggers[_triggers.indexOf(self) - 1];
1122 };
1123
1124 self.next = function () {
1125 return _triggers[_triggers.indexOf(self) + 1];
1126 };
1127
1128 self.revert = function (revert) {
1129 var r = revert !== false || !self.enabled,
1130 prevRefreshing = _refreshing;
1131
1132 if (r !== self.isReverted) {
1133 if (r) {
1134 self.scroll.rec || !_refreshing || !_refreshingAll || (self.scroll.rec = scrollFunc());
1135 prevScroll = Math.max(scrollFunc(), self.scroll.rec || 0); // record the scroll so we can revert later (repositioning/pinning things can affect scroll position). In the static refresh() method, we first record all the scroll positions as a reference.
1136
1137 prevProgress = self.progress;
1138 prevAnimProgress = animation && animation.progress();
1139 }
1140
1141 markerStart && [markerStart, markerEnd, markerStartTrigger, markerEndTrigger].forEach(function (m) {
1142 return m.style.display = r ? "none" : "block";
1143 });
1144 r && (_refreshing = 1);
1145 self.update(r); // make sure the pin is back in its original position so that all the measurements are correct.
1146
1147 _refreshing = prevRefreshing;
1148 pin && (r ? _swapPinOut(pin, spacer, pinOriginalState) : (!pinReparent || !self.isActive) && _swapPinIn(pin, spacer, _getComputedStyle(pin), spacerState));
1149 self.isReverted = r;
1150 }
1151 };
1152
1153 self.refresh = function (soft, force) {
1154 if ((_refreshing || !self.enabled) && !force) {
1155 return;
1156 }
1157
1158 if (pin && soft && _lastScrollTime) {
1159 _addListener(ScrollTrigger, "scrollEnd", _softRefresh);
1160
1161 return;
1162 }
1163
1164 !_refreshingAll && onRefreshInit && onRefreshInit(self);
1165 _refreshing = 1;
1166 lastRefresh = _getTime();
1167
1168 if (tweenTo.tween) {
1169 tweenTo.tween.kill();
1170 tweenTo.tween = 0;
1171 }
1172
1173 scrubTween && scrubTween.pause();
1174 invalidateOnRefresh && animation && animation.time(-0.01, true).invalidate();
1175 self.isReverted || self.revert();
1176
1177 var size = getScrollerSize(),
1178 scrollerBounds = getScrollerOffsets(),
1179 max = containerAnimation ? containerAnimation.duration() : _maxScroll(scroller, direction),
1180 offset = 0,
1181 otherPinOffset = 0,
1182 parsedEnd = vars.end,
1183 parsedEndTrigger = vars.endTrigger || trigger,
1184 parsedStart = vars.start || (vars.start === 0 || !trigger ? 0 : pin ? "0 0" : "0 100%"),
1185 pinnedContainer = self.pinnedContainer = vars.pinnedContainer && _getTarget(vars.pinnedContainer),
1186 triggerIndex = trigger && Math.max(0, _triggers.indexOf(self)) || 0,
1187 i = triggerIndex,
1188 cs,
1189 bounds,
1190 scroll,
1191 isVertical,
1192 override,
1193 curTrigger,
1194 curPin,
1195 oppositeScroll,
1196 initted,
1197 revertedPins;
1198
1199 while (i--) {
1200 // user might try to pin the same element more than once, so we must find any prior triggers with the same pin, revert them, and determine how long they're pinning so that we can offset things appropriately. Make sure we revert from last to first so that things "rewind" properly.
1201 curTrigger = _triggers[i];
1202 curTrigger.end || curTrigger.refresh(0, 1) || (_refreshing = 1); // if it's a timeline-based trigger that hasn't been fully initialized yet because it's waiting for 1 tick, just force the refresh() here, otherwise if it contains a pin that's supposed to affect other ScrollTriggers further down the page, they won't be adjusted properly.
1203
1204 curPin = curTrigger.pin;
1205
1206 if (curPin && (curPin === trigger || curPin === pin) && !curTrigger.isReverted) {
1207 revertedPins || (revertedPins = []);
1208 revertedPins.unshift(curTrigger); // we'll revert from first to last to make sure things reach their end state properly
1209
1210 curTrigger.revert();
1211 }
1212
1213 if (curTrigger !== _triggers[i]) {
1214 // in case it got removed.
1215 triggerIndex--;
1216 i--;
1217 }
1218 }
1219
1220 _isFunction(parsedStart) && (parsedStart = parsedStart(self));
1221 start = _parsePosition(parsedStart, trigger, size, direction, scrollFunc(), markerStart, markerStartTrigger, self, scrollerBounds, borderWidth, useFixedPosition, max, containerAnimation) || (pin ? -0.001 : 0);
1222 _isFunction(parsedEnd) && (parsedEnd = parsedEnd(self));
1223
1224 if (_isString(parsedEnd) && !parsedEnd.indexOf("+=")) {
1225 if (~parsedEnd.indexOf(" ")) {
1226 parsedEnd = (_isString(parsedStart) ? parsedStart.split(" ")[0] : "") + parsedEnd;
1227 } else {
1228 offset = _offsetToPx(parsedEnd.substr(2), size);
1229 parsedEnd = _isString(parsedStart) ? parsedStart : start + offset; // _parsePosition won't factor in the offset if the start is a number, so do it here.
1230
1231 parsedEndTrigger = trigger;
1232 }
1233 }
1234
1235 end = Math.max(start, _parsePosition(parsedEnd || (parsedEndTrigger ? "100% 0" : max), parsedEndTrigger, size, direction, scrollFunc() + offset, markerEnd, markerEndTrigger, self, scrollerBounds, borderWidth, useFixedPosition, max, containerAnimation)) || -0.001;
1236 change = end - start || (start -= 0.01) && 0.001;
1237 offset = 0;
1238 i = triggerIndex;
1239
1240 while (i--) {
1241 curTrigger = _triggers[i];
1242 curPin = curTrigger.pin;
1243
1244 if (curPin && curTrigger.start - curTrigger._pinPush < start && !containerAnimation && curTrigger.end > 0) {
1245 cs = curTrigger.end - curTrigger.start;
1246
1247 if ((curPin === trigger || curPin === pinnedContainer) && !_isNumber(parsedStart)) {
1248 // numeric start values shouldn't be offset at all - treat them as absolute
1249 offset += cs * (1 - curTrigger.progress);
1250 }
1251
1252 curPin === pin && (otherPinOffset += cs);
1253 }
1254 }
1255
1256 start += offset;
1257 end += offset;
1258 self._pinPush = otherPinOffset;
1259
1260 if (markerStart && offset) {
1261 // offset the markers if necessary
1262 cs = {};
1263 cs[direction.a] = "+=" + offset;
1264 pinnedContainer && (cs[direction.p] = "-=" + scrollFunc());
1265 gsap.set([markerStart, markerEnd], cs);
1266 }
1267
1268 if (pin) {
1269 cs = _getComputedStyle(pin);
1270 isVertical = direction === _vertical;
1271 scroll = scrollFunc(); // recalculate because the triggers can affect the scroll
1272
1273 pinStart = parseFloat(pinGetter(direction.a)) + otherPinOffset;
1274 !max && end > 1 && ((isViewport ? _body : scroller).style["overflow-" + direction.a] = "scroll"); // makes sure the scroller has a scrollbar, otherwise if something has width: 100%, for example, it would be too big (exclude the scrollbar). See https://greensock.com/forums/topic/25182-scrolltrigger-width-of-page-increase-where-markers-are-set-to-false/
1275
1276 _swapPinIn(pin, spacer, cs);
1277
1278 pinState = _getState(pin); // transforms will interfere with the top/left/right/bottom placement, so remove them temporarily. getBoundingClientRect() factors in transforms.
1279
1280 bounds = _getBounds(pin, true);
1281 oppositeScroll = useFixedPosition && _getScrollFunc(scroller, isVertical ? _horizontal : _vertical)();
1282
1283 if (pinSpacing) {
1284 spacerState = [pinSpacing + direction.os2, change + otherPinOffset + _px];
1285 spacerState.t = spacer;
1286 i = pinSpacing === _padding ? _getSize(pin, direction) + change + otherPinOffset : 0;
1287 i && spacerState.push(direction.d, i + _px); // for box-sizing: border-box (must include padding).
1288
1289 _setState(spacerState);
1290
1291 useFixedPosition && scrollFunc(prevScroll);
1292 }
1293
1294 if (useFixedPosition) {
1295 override = {
1296 top: bounds.top + (isVertical ? scroll - start : oppositeScroll) + _px,
1297 left: bounds.left + (isVertical ? oppositeScroll : scroll - start) + _px,
1298 boxSizing: "border-box",
1299 position: "fixed"
1300 };
1301 override[_width] = override["max" + _Width] = Math.ceil(bounds.width) + _px;
1302 override[_height] = override["max" + _Height] = Math.ceil(bounds.height) + _px;
1303 override[_margin] = override[_margin + _Top] = override[_margin + _Right] = override[_margin + _Bottom] = override[_margin + _Left] = "0";
1304 override[_padding] = cs[_padding];
1305 override[_padding + _Top] = cs[_padding + _Top];
1306 override[_padding + _Right] = cs[_padding + _Right];
1307 override[_padding + _Bottom] = cs[_padding + _Bottom];
1308 override[_padding + _Left] = cs[_padding + _Left];
1309 pinActiveState = _copyState(pinOriginalState, override, pinReparent);
1310 }
1311
1312 if (animation) {
1313 // the animation might be affecting the transform, so we must jump to the end, check the value, and compensate accordingly. Otherwise, when it becomes unpinned, the pinSetter() will get set to a value that doesn't include whatever the animation did.
1314 initted = animation._initted; // if not, we must invalidate() after this step, otherwise it could lock in starting values prematurely.
1315
1316 _suppressOverwrites(1);
1317
1318 animation.render(animation.duration(), true, true);
1319 pinChange = pinGetter(direction.a) - pinStart + change + otherPinOffset;
1320 change !== pinChange && pinActiveState.splice(pinActiveState.length - 2, 2); // transform is the last property/value set in the state Array. Since the animation is controlling that, we should omit it.
1321
1322 animation.render(0, true, true);
1323 initted || animation.invalidate();
1324
1325 _suppressOverwrites(0);
1326 } else {
1327 pinChange = change;
1328 }
1329 } else if (trigger && scrollFunc() && !containerAnimation) {
1330 // it may be INSIDE a pinned element, so walk up the tree and look for any elements with _pinOffset to compensate because anything with pinSpacing that's already scrolled would throw off the measurements in getBoundingClientRect()
1331 bounds = trigger.parentNode;
1332
1333 while (bounds && bounds !== _body) {
1334 if (bounds._pinOffset) {
1335 start -= bounds._pinOffset;
1336 end -= bounds._pinOffset;
1337 }
1338
1339 bounds = bounds.parentNode;
1340 }
1341 }
1342
1343 revertedPins && revertedPins.forEach(function (t) {
1344 return t.revert(false);
1345 });
1346 self.start = start;
1347 self.end = end;
1348 scroll1 = scroll2 = scrollFunc(); // reset velocity
1349
1350 if (!containerAnimation) {
1351 scroll1 < prevScroll && scrollFunc(prevScroll);
1352 self.scroll.rec = 0;
1353 }
1354
1355 self.revert(false);
1356
1357 if (snapDelayedCall) {
1358 lastSnap = -1;
1359 self.isActive && scrollFunc(start + change * prevProgress); // just so snapping gets re-enabled, clear out any recorded last value
1360
1361 snapDelayedCall.restart(true);
1362 }
1363
1364 _refreshing = 0;
1365 animation && isToggle && (animation._initted || prevAnimProgress) && animation.progress() !== prevAnimProgress && animation.progress(prevAnimProgress, true).render(animation.time(), true, true); // must force a re-render because if saveStyles() was used on the target(s), the styles could have been wiped out during the refresh().
1366
1367 if (prevProgress !== self.progress || containerAnimation) {
1368 // ensures that the direction is set properly (when refreshing, progress is set back to 0 initially, then back again to wherever it needs to be) and that callbacks are triggered.
1369 animation && !isToggle && animation.totalProgress(prevProgress, true); // to avoid issues where animation callbacks like onStart aren't triggered.
1370
1371 self.progress = prevProgress;
1372 self.update(0, 0, 1);
1373 }
1374
1375 pin && pinSpacing && (spacer._pinOffset = Math.round(self.progress * pinChange)); // scrubTween && scrubTween.invalidate();
1376
1377 onRefresh && onRefresh(self);
1378 };
1379
1380 self.getVelocity = function () {
1381 return (scrollFunc() - scroll2) / (_getTime() - _time2) * 1000 || 0;
1382 };
1383
1384 self.endAnimation = function () {
1385 _endAnimation(self.callbackAnimation);
1386
1387 if (animation) {
1388 scrubTween ? scrubTween.progress(1) : !animation.paused() ? _endAnimation(animation, animation.reversed()) : isToggle || _endAnimation(animation, self.direction < 0, 1);
1389 }
1390 };
1391
1392 self.labelToScroll = function (label) {
1393 return animation && animation.labels && (start || self.refresh() || start) + animation.labels[label] / animation.duration() * change || 0;
1394 };
1395
1396 self.getTrailing = function (name) {
1397 var i = _triggers.indexOf(self),
1398 a = self.direction > 0 ? _triggers.slice(0, i).reverse() : _triggers.slice(i + 1);
1399
1400 return (_isString(name) ? a.filter(function (t) {
1401 return t.vars.preventOverlaps === name;
1402 }) : a).filter(function (t) {
1403 return self.direction > 0 ? t.end <= start : t.start >= end;
1404 });
1405 };
1406
1407 self.update = function (reset, recordVelocity, forceFake) {
1408 if (containerAnimation && !forceFake && !reset) {
1409 return;
1410 }
1411
1412 var scroll = self.scroll(),
1413 p = reset ? 0 : (scroll - start) / change,
1414 clipped = p < 0 ? 0 : p > 1 ? 1 : p || 0,
1415 prevProgress = self.progress,
1416 isActive,
1417 wasActive,
1418 toggleState,
1419 action,
1420 stateChanged,
1421 toggled,
1422 isAtMax,
1423 isTakingAction;
1424
1425 if (recordVelocity) {
1426 scroll2 = scroll1;
1427 scroll1 = containerAnimation ? scrollFunc() : scroll;
1428
1429 if (snap) {
1430 snap2 = snap1;
1431 snap1 = animation && !isToggle ? animation.totalProgress() : clipped;
1432 }
1433 } // anticipate the pinning a few ticks ahead of time based on velocity to avoid a visual glitch due to the fact that most browsers do scrolling on a separate thread (not synced with requestAnimationFrame).
1434
1435
1436 anticipatePin && !clipped && pin && !_refreshing && !_startup && _lastScrollTime && start < scroll + (scroll - scroll2) / (_getTime() - _time2) * anticipatePin && (clipped = 0.0001);
1437
1438 if (clipped !== prevProgress && self.enabled) {
1439 isActive = self.isActive = !!clipped && clipped < 1;
1440 wasActive = !!prevProgress && prevProgress < 1;
1441 toggled = isActive !== wasActive;
1442 stateChanged = toggled || !!clipped !== !!prevProgress; // could go from start all the way to end, thus it didn't toggle but it did change state in a sense (may need to fire a callback)
1443
1444 self.direction = clipped > prevProgress ? 1 : -1;
1445 self.progress = clipped;
1446
1447 if (stateChanged && !_refreshing) {
1448 toggleState = clipped && !prevProgress ? 0 : clipped === 1 ? 1 : prevProgress === 1 ? 2 : 3; // 0 = enter, 1 = leave, 2 = enterBack, 3 = leaveBack (we prioritize the FIRST encounter, thus if you scroll really fast past the onEnter and onLeave in one tick, it'd prioritize onEnter.
1449
1450 if (isToggle) {
1451 action = !toggled && toggleActions[toggleState + 1] !== "none" && toggleActions[toggleState + 1] || toggleActions[toggleState]; // if it didn't toggle, that means it shot right past and since we prioritize the "enter" action, we should switch to the "leave" in this case (but only if one is defined)
1452
1453 isTakingAction = animation && (action === "complete" || action === "reset" || action in animation);
1454 }
1455 }
1456
1457 preventOverlaps && (toggled || isTakingAction) && (isTakingAction || scrub || !animation) && (_isFunction(preventOverlaps) ? preventOverlaps(self) : self.getTrailing(preventOverlaps).forEach(function (t) {
1458 return t.endAnimation();
1459 }));
1460
1461 if (!isToggle) {
1462 if (scrubTween && !_refreshing && !_startup) {
1463 (containerAnimation || _primary && _primary !== self) && scrubTween.render(scrubTween._dp._time - scrubTween._start); // if there's a scrub on both the container animation and this one (or a ScrollSmoother), the update order would cause this one not to have rendered yet, so it wouldn't make any progress before we .restart() it heading toward the new progress so it'd appear stuck thus we force a render here.
1464
1465 if (scrubTween.resetTo) {
1466 scrubTween.resetTo("totalProgress", clipped, animation._tTime / animation._tDur);
1467 } else {
1468 // legacy support (courtesy), before 3.10.0
1469 scrubTween.vars.totalProgress = clipped;
1470 scrubTween.invalidate().restart();
1471 }
1472 } else if (animation) {
1473 animation.totalProgress(clipped, !!_refreshing);
1474 }
1475 }
1476
1477 if (pin) {
1478 reset && pinSpacing && (spacer.style[pinSpacing + direction.os2] = spacingStart);
1479
1480 if (!useFixedPosition) {
1481 pinSetter(_round(pinStart + pinChange * clipped));
1482 } else if (stateChanged) {
1483 isAtMax = !reset && clipped > prevProgress && end + 1 > scroll && scroll + 1 >= _maxScroll(scroller, direction); // if it's at the VERY end of the page, don't switch away from position: fixed because it's pointless and it could cause a brief flash when the user scrolls back up (when it gets pinned again)
1484
1485 if (pinReparent) {
1486 if (!reset && (isActive || isAtMax)) {
1487 var bounds = _getBounds(pin, true),
1488 _offset = scroll - start;
1489
1490 _reparent(pin, _body, bounds.top + (direction === _vertical ? _offset : 0) + _px, bounds.left + (direction === _vertical ? 0 : _offset) + _px);
1491 } else {
1492 _reparent(pin, spacer);
1493 }
1494 }
1495
1496 _setState(isActive || isAtMax ? pinActiveState : pinState);
1497
1498 pinChange !== change && clipped < 1 && isActive || pinSetter(pinStart + (clipped === 1 && !isAtMax ? pinChange : 0));
1499 }
1500 }
1501
1502 snap && !tweenTo.tween && !_refreshing && !_startup && snapDelayedCall.restart(true);
1503 toggleClass && (toggled || once && clipped && (clipped < 1 || !_limitCallbacks)) && _toArray(toggleClass.targets).forEach(function (el) {
1504 return el.classList[isActive || once ? "add" : "remove"](toggleClass.className);
1505 }); // classes could affect positioning, so do it even if reset or refreshing is true.
1506
1507 onUpdate && !isToggle && !reset && onUpdate(self);
1508
1509 if (stateChanged && !_refreshing) {
1510 if (isToggle) {
1511 if (isTakingAction) {
1512 if (action === "complete") {
1513 animation.pause().totalProgress(1);
1514 } else if (action === "reset") {
1515 animation.restart(true).pause();
1516 } else if (action === "restart") {
1517 animation.restart(true);
1518 } else {
1519 animation[action]();
1520 }
1521 }
1522
1523 onUpdate && onUpdate(self);
1524 }
1525
1526 if (toggled || !_limitCallbacks) {
1527 // on startup, the page could be scrolled and we don't want to fire callbacks that didn't toggle. For example onEnter shouldn't fire if the ScrollTrigger isn't actually entered.
1528 onToggle && toggled && _callback(self, onToggle);
1529 callbacks[toggleState] && _callback(self, callbacks[toggleState]);
1530 once && (clipped === 1 ? self.kill(false, 1) : callbacks[toggleState] = 0); // a callback shouldn't be called again if once is true.
1531
1532 if (!toggled) {
1533 // it's possible to go completely past, like from before the start to after the end (or vice-versa) in which case BOTH callbacks should be fired in that order
1534 toggleState = clipped === 1 ? 1 : 3;
1535 callbacks[toggleState] && _callback(self, callbacks[toggleState]);
1536 }
1537 }
1538
1539 if (fastScrollEnd && !isActive && Math.abs(self.getVelocity()) > (_isNumber(fastScrollEnd) ? fastScrollEnd : 2500)) {
1540 _endAnimation(self.callbackAnimation);
1541
1542 scrubTween ? scrubTween.progress(1) : _endAnimation(animation, !clipped, 1);
1543 }
1544 } else if (isToggle && onUpdate && !_refreshing) {
1545 onUpdate(self);
1546 }
1547 } // update absolutely-positioned markers (only if the scroller isn't the viewport)
1548
1549
1550 if (markerEndSetter) {
1551 var n = containerAnimation ? scroll / containerAnimation.duration() * (containerAnimation._caScrollDist || 0) : scroll;
1552 markerStartSetter(n + (markerStartTrigger._isFlipped ? 1 : 0));
1553 markerEndSetter(n);
1554 }
1555
1556 caMarkerSetter && caMarkerSetter(-scroll / containerAnimation.duration() * (containerAnimation._caScrollDist || 0));
1557 };
1558
1559 self.enable = function (reset, refresh) {
1560 if (!self.enabled) {
1561 self.enabled = true;
1562
1563 _addListener(scroller, "resize", _onResize);
1564
1565 _addListener(isViewport ? _doc : scroller, "scroll", _onScroll);
1566
1567 onRefreshInit && _addListener(ScrollTrigger, "refreshInit", onRefreshInit);
1568
1569 if (reset !== false) {
1570 self.progress = prevProgress = 0;
1571 scroll1 = scroll2 = lastSnap = scrollFunc();
1572 }
1573
1574 refresh !== false && self.refresh();
1575 }
1576 };
1577
1578 self.getTween = function (snap) {
1579 return snap && tweenTo ? tweenTo.tween : scrubTween;
1580 };
1581
1582 self.setPositions = function (newStart, newEnd) {
1583 // doesn't persist after refresh()! Intended to be a way to override values that were set during refresh(), like you could set it in onRefresh()
1584 if (pin) {
1585 pinStart += newStart - start;
1586 pinChange += newEnd - newStart - change;
1587 }
1588
1589 self.start = start = newStart;
1590 self.end = end = newEnd;
1591 change = newEnd - newStart;
1592 self.update();
1593 };
1594
1595 self.disable = function (reset, allowAnimation) {
1596 if (self.enabled) {
1597 reset !== false && self.revert();
1598 self.enabled = self.isActive = false;
1599 allowAnimation || scrubTween && scrubTween.pause();
1600 prevScroll = 0;
1601 pinCache && (pinCache.uncache = 1);
1602 onRefreshInit && _removeListener(ScrollTrigger, "refreshInit", onRefreshInit);
1603
1604 if (snapDelayedCall) {
1605 snapDelayedCall.pause();
1606 tweenTo.tween && tweenTo.tween.kill() && (tweenTo.tween = 0);
1607 }
1608
1609 if (!isViewport) {
1610 var i = _triggers.length;
1611
1612 while (i--) {
1613 if (_triggers[i].scroller === scroller && _triggers[i] !== self) {
1614 return; //don't remove the listeners if there are still other triggers referencing it.
1615 }
1616 }
1617
1618 _removeListener(scroller, "resize", _onResize);
1619
1620 _removeListener(scroller, "scroll", _onScroll);
1621 }
1622 }
1623 };
1624
1625 self.kill = function (revert, allowAnimation) {
1626 self.disable(revert, allowAnimation);
1627 scrubTween && !allowAnimation && scrubTween.kill();
1628 id && delete _ids[id];
1629
1630 var i = _triggers.indexOf(self);
1631
1632 i >= 0 && _triggers.splice(i, 1);
1633 i === _i && _direction > 0 && _i--; // if we're in the middle of a refresh() or update(), splicing would cause skips in the index, so adjust...
1634 // if no other ScrollTrigger instances of the same scroller are found, wipe out any recorded scroll position. Otherwise, in a single page application, for example, it could maintain scroll position when it really shouldn't.
1635
1636 i = 0;
1637
1638 _triggers.forEach(function (t) {
1639 return t.scroller === self.scroller && (i = 1);
1640 });
1641
1642 i || (self.scroll.rec = 0);
1643
1644 if (animation) {
1645 animation.scrollTrigger = null;
1646 revert && animation.render(-1);
1647 allowAnimation || animation.kill();
1648 }
1649
1650 markerStart && [markerStart, markerEnd, markerStartTrigger, markerEndTrigger].forEach(function (m) {
1651 return m.parentNode && m.parentNode.removeChild(m);
1652 });
1653 _primary === self && (_primary = 0);
1654
1655 if (pin) {
1656 pinCache && (pinCache.uncache = 1);
1657 i = 0;
1658
1659 _triggers.forEach(function (t) {
1660 return t.pin === pin && i++;
1661 });
1662
1663 i || (pinCache.spacer = 0); // if there aren't any more ScrollTriggers with the same pin, remove the spacer, otherwise it could be contaminated with old/stale values if the user re-creates a ScrollTrigger for the same element.
1664 }
1665
1666 vars.onKill && vars.onKill(self);
1667 };
1668
1669 self.enable(false, false);
1670 customRevertReturn && customRevertReturn(self);
1671 !animation || !animation.add || change ? self.refresh() : gsap.delayedCall(0.01, function () {
1672 return start || end || self.refresh();
1673 }) && (change = 0.01) && (start = end = 0); // if the animation is a timeline, it may not have been populated yet, so it wouldn't render at the proper place on the first refresh(), thus we should schedule one for the next tick. If "change" is defined, we know it must be re-enabling, thus we can refresh() right away.
1674 };
1675
1676 ScrollTrigger.register = function register(core) {
1677 if (!_coreInitted) {
1678 gsap = core || _getGSAP();
1679 _windowExists() && window.document && ScrollTrigger.enable();
1680 _coreInitted = _enabled;
1681 }
1682
1683 return _coreInitted;
1684 };
1685
1686 ScrollTrigger.defaults = function defaults(config) {
1687 if (config) {
1688 for (var p in config) {
1689 _defaults[p] = config[p];
1690 }
1691 }
1692
1693 return _defaults;
1694 };
1695
1696 ScrollTrigger.disable = function disable(reset, kill) {
1697 _enabled = 0;
1698
1699 _triggers.forEach(function (trigger) {
1700 return trigger[kill ? "kill" : "disable"](reset);
1701 });
1702
1703 _removeListener(_win, "wheel", _onScroll);
1704
1705 _removeListener(_doc, "scroll", _onScroll);
1706
1707 clearInterval(_syncInterval);
1708
1709 _removeListener(_doc, "touchcancel", _passThrough);
1710
1711 _removeListener(_body, "touchstart", _passThrough);
1712
1713 _multiListener(_removeListener, _doc, "pointerdown,touchstart,mousedown", _pointerDownHandler);
1714
1715 _multiListener(_removeListener, _doc, "pointerup,touchend,mouseup", _pointerUpHandler);
1716
1717 _resizeDelay.kill();
1718
1719 _iterateAutoRefresh(_removeListener);
1720
1721 for (var i = 0; i < _scrollers.length; i += 3) {
1722 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 1]);
1723
1724 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 2]);
1725 }
1726 };
1727
1728 ScrollTrigger.enable = function enable() {
1729 _win = window;
1730 _doc = document;
1731 _docEl = _doc.documentElement;
1732 _body = _doc.body;
1733
1734 if (gsap) {
1735 _toArray = gsap.utils.toArray;
1736 _clamp = gsap.utils.clamp;
1737 _suppressOverwrites = gsap.core.suppressOverwrites || _passThrough;
1738 gsap.core.globals("ScrollTrigger", ScrollTrigger); // must register the global manually because in Internet Explorer, functions (classes) don't have a "name" property.
1739
1740 if (_body) {
1741 _enabled = 1;
1742 Observer.register(gsap); // isTouch is 0 if no touch, 1 if ONLY touch, and 2 if it can accommodate touch but also other types like mouse/pointer.
1743
1744 ScrollTrigger.isTouch = Observer.isTouch;
1745
1746 _addListener(_win, "wheel", _onScroll); // mostly for 3rd party smooth scrolling libraries.
1747
1748
1749 _root = [_win, _doc, _docEl, _body];
1750 ScrollTrigger.matchMedia({
1751 // when orientation changes, we should take new base measurements for the ignoreMobileResize feature.
1752 "(orientation: portrait)": function orientationPortrait() {
1753 _setBaseDimensions();
1754
1755 return _setBaseDimensions;
1756 }
1757 });
1758
1759 _addListener(_doc, "scroll", _onScroll); // some browsers (like Chrome), the window stops dispatching scroll events on the window if you scroll really fast, but it's consistent on the document!
1760
1761
1762 var bodyStyle = _body.style,
1763 border = bodyStyle.borderTopStyle,
1764 bounds,
1765 i;
1766 bodyStyle.borderTopStyle = "solid"; // works around an issue where a margin of a child element could throw off the bounds of the _body, making it seem like there's a margin when there actually isn't. The border ensures that the bounds are accurate.
1767
1768 bounds = _getBounds(_body);
1769 _vertical.m = Math.round(bounds.top + _vertical.sc()) || 0; // accommodate the offset of the <body> caused by margins and/or padding
1770
1771 _horizontal.m = Math.round(bounds.left + _horizontal.sc()) || 0;
1772 border ? bodyStyle.borderTopStyle = border : bodyStyle.removeProperty("border-top-style"); // TODO: (?) maybe move to leveraging the velocity mechanism in Observer and skip intervals.
1773
1774 _syncInterval = setInterval(_sync, 250);
1775 gsap.delayedCall(0.5, function () {
1776 return _startup = 0;
1777 });
1778
1779 _addListener(_doc, "touchcancel", _passThrough); // some older Android devices intermittently stop dispatching "touchmove" events if we don't listen for "touchcancel" on the document.
1780
1781
1782 _addListener(_body, "touchstart", _passThrough); //works around Safari bug: https://greensock.com/forums/topic/21450-draggable-in-iframe-on-mobile-is-buggy/
1783
1784
1785 _multiListener(_addListener, _doc, "pointerdown,touchstart,mousedown", _pointerDownHandler);
1786
1787 _multiListener(_addListener, _doc, "pointerup,touchend,mouseup", _pointerUpHandler);
1788
1789 _transformProp = gsap.utils.checkPrefix("transform");
1790
1791 _stateProps.push(_transformProp);
1792
1793 _coreInitted = _getTime();
1794 _resizeDelay = gsap.delayedCall(0.2, _refreshAll).pause();
1795 _autoRefresh = [_doc, "visibilitychange", function () {
1796 var w = _win.innerWidth,
1797 h = _win.innerHeight;
1798
1799 if (_doc.hidden) {
1800 _prevWidth = w;
1801 _prevHeight = h;
1802 } else if (_prevWidth !== w || _prevHeight !== h) {
1803 _onResize();
1804 }
1805 }, _doc, "DOMContentLoaded", _refreshAll, _win, "load", _refreshAll, _win, "resize", _onResize];
1806
1807 _iterateAutoRefresh(_addListener);
1808
1809 _triggers.forEach(function (trigger) {
1810 return trigger.enable(0, 1);
1811 });
1812
1813 for (i = 0; i < _scrollers.length; i += 3) {
1814 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 1]);
1815
1816 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 2]);
1817 }
1818 }
1819 }
1820 };
1821
1822 ScrollTrigger.config = function config(vars) {
1823 "limitCallbacks" in vars && (_limitCallbacks = !!vars.limitCallbacks);
1824 var ms = vars.syncInterval;
1825 ms && clearInterval(_syncInterval) || (_syncInterval = ms) && setInterval(_sync, ms);
1826 "ignoreMobileResize" in vars && (_ignoreMobileResize = ScrollTrigger.isTouch === 1 && vars.ignoreMobileResize);
1827
1828 if ("autoRefreshEvents" in vars) {
1829 _iterateAutoRefresh(_removeListener) || _iterateAutoRefresh(_addListener, vars.autoRefreshEvents || "none");
1830 _ignoreResize = (vars.autoRefreshEvents + "").indexOf("resize") === -1;
1831 }
1832 };
1833
1834 ScrollTrigger.scrollerProxy = function scrollerProxy(target, vars) {
1835 var t = _getTarget(target),
1836 i = _scrollers.indexOf(t),
1837 isViewport = _isViewport(t);
1838
1839 if (~i) {
1840 _scrollers.splice(i, isViewport ? 6 : 2);
1841 }
1842
1843 if (vars) {
1844 isViewport ? _proxies.unshift(_win, vars, _body, vars, _docEl, vars) : _proxies.unshift(t, vars);
1845 }
1846 };
1847
1848 ScrollTrigger.matchMedia = function matchMedia(vars) {
1849 // _media is populated in the following order: mediaQueryString, onMatch, onUnmatch, isMatched. So if there are two media queries, the Array would have a length of 8
1850 var mq, p, i, func, result;
1851
1852 for (p in vars) {
1853 i = _media.indexOf(p);
1854 func = vars[p];
1855 _creatingMedia = p;
1856
1857 if (p === "all") {
1858 func();
1859 } else {
1860 mq = _win.matchMedia(p);
1861
1862 if (mq) {
1863 mq.matches && (result = func());
1864
1865 if (~i) {
1866 _media[i + 1] = _combineFunc(_media[i + 1], func);
1867 _media[i + 2] = _combineFunc(_media[i + 2], result);
1868 } else {
1869 i = _media.length;
1870
1871 _media.push(p, func, result);
1872
1873 mq.addListener ? mq.addListener(_onMediaChange) : mq.addEventListener("change", _onMediaChange);
1874 }
1875
1876 _media[i + 3] = mq.matches;
1877 }
1878 }
1879
1880 _creatingMedia = 0;
1881 }
1882
1883 return _media;
1884 };
1885
1886 ScrollTrigger.clearMatchMedia = function clearMatchMedia(query) {
1887 query || (_media.length = 0);
1888 query = _media.indexOf(query);
1889 query >= 0 && _media.splice(query, 4);
1890 };
1891
1892 ScrollTrigger.isInViewport = function isInViewport(element, ratio, horizontal) {
1893 var bounds = (_isString(element) ? _getTarget(element) : element).getBoundingClientRect(),
1894 offset = bounds[horizontal ? _width : _height] * ratio || 0;
1895 return horizontal ? bounds.right - offset > 0 && bounds.left + offset < _win.innerWidth : bounds.bottom - offset > 0 && bounds.top + offset < _win.innerHeight;
1896 };
1897
1898 ScrollTrigger.positionInViewport = function positionInViewport(element, referencePoint, horizontal) {
1899 _isString(element) && (element = _getTarget(element));
1900 var bounds = element.getBoundingClientRect(),
1901 size = bounds[horizontal ? _width : _height],
1902 offset = referencePoint == null ? size / 2 : referencePoint in _keywords ? _keywords[referencePoint] * size : ~referencePoint.indexOf("%") ? parseFloat(referencePoint) * size / 100 : parseFloat(referencePoint) || 0;
1903 return horizontal ? (bounds.left + offset) / _win.innerWidth : (bounds.top + offset) / _win.innerHeight;
1904 };
1905
1906 return ScrollTrigger;
1907}();
1908ScrollTrigger.version = "3.10.3";
1909
1910ScrollTrigger.saveStyles = function (targets) {
1911 return targets ? _toArray(targets).forEach(function (target) {
1912 // saved styles are recorded in a consecutive alternating Array, like [element, cssText, transform attribute, cache, matchMedia, ...]
1913 if (target && target.style) {
1914 var i = _savedStyles.indexOf(target);
1915
1916 i >= 0 && _savedStyles.splice(i, 5);
1917
1918 _savedStyles.push(target, target.style.cssText, target.getBBox && target.getAttribute("transform"), gsap.core.getCache(target), _creatingMedia);
1919 }
1920 }) : _savedStyles;
1921};
1922
1923ScrollTrigger.revert = function (soft, media) {
1924 return _revertAll(!soft, media);
1925};
1926
1927ScrollTrigger.create = function (vars, animation) {
1928 return new ScrollTrigger(vars, animation);
1929};
1930
1931ScrollTrigger.refresh = function (safe) {
1932 return safe ? _onResize() : (_coreInitted || ScrollTrigger.register()) && _refreshAll(true);
1933};
1934
1935ScrollTrigger.update = _updateAll;
1936ScrollTrigger.clearScrollMemory = _clearScrollMemory;
1937
1938ScrollTrigger.maxScroll = function (element, horizontal) {
1939 return _maxScroll(element, horizontal ? _horizontal : _vertical);
1940};
1941
1942ScrollTrigger.getScrollFunc = function (element, horizontal) {
1943 return _getScrollFunc(_getTarget(element), horizontal ? _horizontal : _vertical);
1944};
1945
1946ScrollTrigger.getById = function (id) {
1947 return _ids[id];
1948};
1949
1950ScrollTrigger.getAll = function () {
1951 return _triggers.filter(function (t) {
1952 return t.vars.id !== "ScrollSmoother";
1953 });
1954}; // it's common for people to ScrollTrigger.getAll(t => t.kill()) on page routes, for example, and we don't want it to ruin smooth scrolling by killing the main ScrollSmoother one.
1955
1956
1957ScrollTrigger.isScrolling = function () {
1958 return !!_lastScrollTime;
1959};
1960
1961ScrollTrigger.snapDirectional = _snapDirectional;
1962
1963ScrollTrigger.addEventListener = function (type, callback) {
1964 var a = _listeners[type] || (_listeners[type] = []);
1965 ~a.indexOf(callback) || a.push(callback);
1966};
1967
1968ScrollTrigger.removeEventListener = function (type, callback) {
1969 var a = _listeners[type],
1970 i = a && a.indexOf(callback);
1971 i >= 0 && a.splice(i, 1);
1972};
1973
1974ScrollTrigger.batch = function (targets, vars) {
1975 var result = [],
1976 varsCopy = {},
1977 interval = vars.interval || 0.016,
1978 batchMax = vars.batchMax || 1e9,
1979 proxyCallback = function proxyCallback(type, callback) {
1980 var elements = [],
1981 triggers = [],
1982 delay = gsap.delayedCall(interval, function () {
1983 callback(elements, triggers);
1984 elements = [];
1985 triggers = [];
1986 }).pause();
1987 return function (self) {
1988 elements.length || delay.restart(true);
1989 elements.push(self.trigger);
1990 triggers.push(self);
1991 batchMax <= elements.length && delay.progress(1);
1992 };
1993 },
1994 p;
1995
1996 for (p in vars) {
1997 varsCopy[p] = p.substr(0, 2) === "on" && _isFunction(vars[p]) && p !== "onRefreshInit" ? proxyCallback(p, vars[p]) : vars[p];
1998 }
1999
2000 if (_isFunction(batchMax)) {
2001 batchMax = batchMax();
2002
2003 _addListener(ScrollTrigger, "refresh", function () {
2004 return batchMax = vars.batchMax();
2005 });
2006 }
2007
2008 _toArray(targets).forEach(function (target) {
2009 var config = {};
2010
2011 for (p in varsCopy) {
2012 config[p] = varsCopy[p];
2013 }
2014
2015 config.trigger = target;
2016 result.push(ScrollTrigger.create(config));
2017 });
2018
2019 return result;
2020}; // to reduce file size. clamps the scroll and also returns a duration multiplier so that if the scroll gets chopped shorter, the duration gets curtailed as well (otherwise if you're very close to the top of the page, for example, and swipe up really fast, it'll suddenly slow down and take a long time to reach the top).
2021
2022
2023var _clampScrollAndGetDurationMultiplier = function _clampScrollAndGetDurationMultiplier(scrollFunc, current, end, max) {
2024 current > max ? scrollFunc(max) : current < 0 && scrollFunc(0);
2025 return end > max ? (max - current) / (end - current) : end < 0 ? current / (current - end) : 1;
2026},
2027 _allowNativePanning = function _allowNativePanning(target, direction) {
2028 if (direction === true) {
2029 target.style.removeProperty("touch-action");
2030 } else {
2031 target.style.touchAction = direction ? "pan-" + direction : "none"; // note: we tried adding pinch-zoom too, but Firefox doesn't support it properly.
2032 }
2033
2034 target === _docEl && _allowNativePanning(_body);
2035},
2036 _overflow = {
2037 auto: 1,
2038 scroll: 1
2039},
2040 _nestedScroll = function _nestedScroll(_ref5) {
2041 var event = _ref5.event,
2042 target = _ref5.target,
2043 axis = _ref5.axis;
2044
2045 var node = (event.changedTouches ? event.changedTouches[0] : event).target,
2046 cache = node._gsap || gsap.core.getCache(node),
2047 time = _getTime(),
2048 cs;
2049
2050 if (!cache._isScrollT || time - cache._isScrollT > 2000) {
2051 // cache for 2 seconds to improve performance.
2052 while (node && node.scrollHeight <= node.clientHeight) {
2053 node = node.parentNode;
2054 }
2055
2056 cache._isScroll = node && !_isViewport(node) && node !== target && (_overflow[(cs = _getComputedStyle(node)).overflowY] || _overflow[cs.overflowX]);
2057 cache._isScrollT = time;
2058 }
2059
2060 (cache._isScroll || axis === "x") && (event._gsapAllow = true);
2061},
2062 // capture events on scrollable elements INSIDE the <body> and allow those by calling stopPropagation() when we find a scrollable ancestor
2063_inputObserver = function _inputObserver(target, type, inputs, nested) {
2064 return Observer.create({
2065 target: target,
2066 capture: true,
2067 debounce: false,
2068 lockAxis: true,
2069 type: type,
2070 onWheel: nested = nested && _nestedScroll,
2071 onPress: nested,
2072 onDrag: nested,
2073 onScroll: nested,
2074 onEnable: function onEnable() {
2075 return inputs && _addListener(_doc, Observer.eventTypes[0], _captureInputs, false, true);
2076 },
2077 onDisable: function onDisable() {
2078 return _removeListener(_doc, Observer.eventTypes[0], _captureInputs);
2079 }
2080 });
2081},
2082 _inputExp = /(input|label|select|textarea)/i,
2083 _inputIsFocused,
2084 _captureInputs = function _captureInputs(e) {
2085 var isInput = _inputExp.test(e.target.tagName);
2086
2087 if (isInput || _inputIsFocused) {
2088 e._gsapAllow = true;
2089 _inputIsFocused = isInput;
2090 }
2091},
2092 _getScrollNormalizer = function _getScrollNormalizer(vars) {
2093 _isObject(vars) || (vars = {});
2094 vars.preventDefault = vars.isNormalizer = vars.allowClicks = true;
2095 vars.type || (vars.type = "wheel,touch");
2096 vars.debounce = !!vars.debounce;
2097 vars.id = vars.id || "normalizer";
2098
2099 var _vars2 = vars,
2100 normalizeScrollX = _vars2.normalizeScrollX,
2101 momentum = _vars2.momentum,
2102 allowNestedScroll = _vars2.allowNestedScroll,
2103 self,
2104 maxY,
2105 target = _getTarget(vars.target) || _docEl,
2106 scrollFuncY = _getScrollFunc(target, _vertical),
2107 scrollFuncX = _getScrollFunc(target, _horizontal),
2108 scale = 1,
2109 wheelRefresh = 0,
2110 resolveMomentumDuration = _isFunction(momentum) ? function () {
2111 return momentum(self);
2112 } : function () {
2113 return momentum || 2.8;
2114 },
2115 skipTouchMove,
2116 lastRefreshID,
2117 inputObserver = _inputObserver(target, vars.type, true, allowNestedScroll),
2118 resumeTouchMove = function resumeTouchMove() {
2119 return skipTouchMove = false;
2120 },
2121 scrollClampX = _passThrough,
2122 scrollClampY = _passThrough,
2123 updateClamps = function updateClamps() {
2124 maxY = _maxScroll(target, _vertical);
2125 scrollClampY = _clamp(0, maxY);
2126 normalizeScrollX && (scrollClampX = _clamp(0, _maxScroll(target, _horizontal)));
2127 lastRefreshID = _refreshID;
2128 },
2129 fixIOSBug = ScrollTrigger.isTouch && /(iPad|iPhone|iPod|Mac)/g.test(navigator.userAgent),
2130 ignoreDrag = function ignoreDrag() {
2131 if (skipTouchMove) {
2132 requestAnimationFrame(resumeTouchMove); // we MUST wait for a requestAnimationFrame, otherwise iOS will misreport the value.
2133
2134 return true;
2135 }
2136
2137 skipTouchMove = true;
2138 },
2139 tween,
2140 startScrollX,
2141 startScrollY,
2142 onStopDelayedCall,
2143 onResize = function onResize() {
2144 // if the window resizes, like on an iPhone which Apple FORCES the address bar to show/hide even if we event.preventDefault(), it may be scrolling too far now that the address bar is showing, so we must dynamically adjust the momentum tween.
2145 updateClamps();
2146 tween.isActive() && tween.vars.scrollY > maxY && tween.resetTo("scrollY", _maxScroll(target, _vertical));
2147 };
2148
2149 vars.ignoreCheck = function (e) {
2150 return fixIOSBug && e.type === "touchmove" && ignoreDrag(e) || scale > 1 || self.isGesturing || e.touches && e.touches.length > 1;
2151 };
2152
2153 vars.onPress = function () {
2154 var prevScale = scale;
2155 scale = _win.visualViewport && _win.visualViewport.scale || 1;
2156 tween.pause();
2157 prevScale !== scale && _allowNativePanning(target, scale > 1 ? true : normalizeScrollX ? false : "x");
2158 skipTouchMove = false;
2159 startScrollX = scrollFuncX();
2160 startScrollY = scrollFuncY();
2161 updateClamps();
2162 lastRefreshID = _refreshID;
2163 };
2164
2165 vars.onRelease = vars.onGestureStart = function (self, wasDragging) {
2166 if (!wasDragging) {
2167 onStopDelayedCall.restart(true);
2168 } else {
2169 // alternate algorithm: durX = Math.min(6, Math.abs(self.velocityX / 800)), dur = Math.max(durX, Math.min(6, Math.abs(self.velocityY / 800))); dur = dur * (0.4 + (1 - _power4In(dur / 6)) * 0.6)) * (momentumSpeed || 1)
2170 var dur = resolveMomentumDuration(),
2171 currentScroll,
2172 endScroll;
2173
2174 if (normalizeScrollX) {
2175 currentScroll = scrollFuncX();
2176 endScroll = currentScroll + dur * 0.05 * -self.velocityX / 0.227 / scale; // the constant .227 is from power4(0.05). velocity is inverted because scrolling goes in the opposite direction.
2177
2178 dur *= _clampScrollAndGetDurationMultiplier(scrollFuncX, currentScroll, endScroll, _maxScroll(target, _horizontal));
2179 tween.vars.scrollX = scrollClampX(endScroll);
2180 }
2181
2182 currentScroll = scrollFuncY();
2183 endScroll = currentScroll + dur * 0.05 * -self.velocityY / 0.227 / scale; // the constant .227 is from power4(0.05)
2184
2185 dur *= _clampScrollAndGetDurationMultiplier(scrollFuncY, currentScroll, endScroll, _maxScroll(target, _vertical));
2186 tween.vars.scrollY = scrollClampY(endScroll);
2187 tween.invalidate().duration(dur).play(0.01);
2188 }
2189 };
2190
2191 vars.onWheel = function () {
2192 tween._ts && tween.pause();
2193
2194 if (_getTime() - wheelRefresh > 1000) {
2195 // after 1 second, refresh the clamps otherwise that'll only happen when ScrollTrigger.refresh() is called or for touch-scrolling.
2196 lastRefreshID = 0;
2197 wheelRefresh = _getTime();
2198 }
2199 };
2200
2201 vars.onChange = function (self, dx, dy, xArray, yArray) {
2202 _refreshID !== lastRefreshID && updateClamps();
2203 dx && normalizeScrollX && scrollFuncX(scrollClampX(xArray[2] === dx ? startScrollX + (self.startX - self.x) / scale : scrollFuncX() + dx - xArray[1])); // for more precision, we track pointer/touch movement from the start, otherwise it'll drift.
2204
2205 dy && scrollFuncY(scrollClampY(yArray[2] === dy ? startScrollY + (self.startY - self.y) / scale : scrollFuncY() + dy - yArray[1]));
2206
2207 _updateAll();
2208 };
2209
2210 vars.onEnable = function () {
2211 _allowNativePanning(target, normalizeScrollX ? false : "x");
2212
2213 _addListener(_win, "resize", onResize);
2214
2215 inputObserver.enable();
2216 };
2217
2218 vars.onDisable = function () {
2219 _allowNativePanning(target, true);
2220
2221 _removeListener(_win, "resize", onResize);
2222
2223 inputObserver.kill();
2224 };
2225
2226 self = new Observer(vars);
2227 onStopDelayedCall = self._dc;
2228 tween = gsap.to(self, {
2229 ease: "power4",
2230 paused: true,
2231 scrollX: normalizeScrollX ? "+=0.1" : "+=0",
2232 scrollY: "+=0.1",
2233 onComplete: onStopDelayedCall.vars.onComplete
2234 });
2235 return self;
2236};
2237
2238ScrollTrigger.sort = function (func) {
2239 return _triggers.sort(func || function (a, b) {
2240 return (a.vars.refreshPriority || 0) * -1e6 + a.start - (b.start + (b.vars.refreshPriority || 0) * -1e6);
2241 });
2242};
2243
2244ScrollTrigger.observe = function (vars) {
2245 return new Observer(vars);
2246};
2247
2248ScrollTrigger.normalizeScroll = function (vars) {
2249 if (typeof vars === "undefined") {
2250 return _normalizer;
2251 }
2252
2253 if (vars === true && _normalizer) {
2254 return _normalizer.enable();
2255 }
2256
2257 if (vars === false) {
2258 return _normalizer && _normalizer.kill();
2259 }
2260
2261 var normalizer = vars instanceof Observer ? vars : _getScrollNormalizer(vars);
2262 _normalizer && _normalizer.target === normalizer.target && _normalizer.kill();
2263 _isViewport(normalizer.target) && (_normalizer = normalizer);
2264 return normalizer;
2265};
2266
2267ScrollTrigger.core = {
2268 // smaller file size way to leverage in ScrollSmoother and Observer
2269 _getVelocityProp: _getVelocityProp,
2270 _inputObserver: _inputObserver,
2271 _scrollers: _scrollers,
2272 _proxies: _proxies,
2273 bridge: {
2274 // when normalizeScroll sets the scroll position (ss = setScroll)
2275 ss: function ss() {
2276 _lastScrollTime || _dispatch("scrollStart");
2277 _lastScrollTime = _getTime();
2278 },
2279 // a way to get the _refreshing value in Observer
2280 ref: function ref() {
2281 return _refreshing;
2282 }
2283 }
2284};
2285_getGSAP() && gsap.registerPlugin(ScrollTrigger);
2286export { ScrollTrigger as default };
\No newline at end of file