vendor: 4,325 bytes, lines 1-120
1/*! 2 * ScrollTrigger 3.12.2 3 * https://greensock.com 4 * 5 * @license Copyright 2008-2023, 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, [email protected] 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 _fixIOSBug, 41 _context, 42 _scrollRestoration, 43 _div100vh, 44 _100vh, 45 _limitCallbacks, 46 // 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. 47_startup = 1, 48 _getTime = Date.now, 49 _time1 = _getTime(), 50 _lastScrollTime = 0, 51 _enabled = 0, 52 _parseClamp = function _parseClamp(value, type, self) { 53 var clamp = _isString(value) && (value.substr(0, 6) === "clamp(" || value.indexOf("max") > -1); 54 self["_" + type + "Clamp"] = clamp; 55 return clamp ? value.substr(6, value.length - 7) : value; 56}, 57 _keepClamp = function _keepClamp(value, clamp) { 58 return clamp && (!_isString(value) || value.substr(0, 6) !== "clamp(") ? "clamp(" + value + ")" : value; 59}, 60 _rafBugFix = function _rafBugFix() { 61 return _enabled && requestAnimationFrame(_rafBugFix); 62}, 63 // in some browsers (like Firefox), screen repaints weren't consistent unless we had SOMETHING queued up in requestAnimationFrame()! So this just creates a super simple loop to keep it alive and smooth out repaints. 64_pointerDownHandler = function _pointerDownHandler() { 65 return _pointerIsDown = 1; 66}, 67 _pointerUpHandler = function _pointerUpHandler() { 68 return _pointerIsDown = 0; 69}, 70 _passThrough = function _passThrough(v) { 71 return v; 72}, 73 _round = function _round(value) { 74 return Math.round(value * 100000) / 100000 || 0; 75}, 76 _windowExists = function _windowExists() { 77 return typeof window !== "undefined"; 78}, 79 _getGSAP = function _getGSAP() { 80 return gsap || _windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap; 81}, 82 _isViewport = function _isViewport(e) { 83 return !!~_root.indexOf(e); 84}, 85 _getViewportDimension = function _getViewportDimension(dimensionProperty) { 86 return (dimensionProperty === "Height" ? _100vh : _win["inner" + dimensionProperty]) || _docEl["client" + dimensionProperty] || _body["client" + dimensionProperty]; 87}, 88 _getBoundsFunc = function _getBoundsFunc(element) { 89 return _getProxyProp(element, "getBoundingClientRect") || (_isViewport(element) ? function () { 90 _winOffsets.width = _win.innerWidth; 91 _winOffsets.height = _100vh; 92 return _winOffsets; 93 } : function () { 94 return _getBounds(element); 95 }); 96}, 97 _getSizeFunc = function _getSizeFunc(scroller, isViewport, _ref) { 98 var d = _ref.d, 99 d2 = _ref.d2, 100 a = _ref.a; 101 return (a = _getProxyProp(scroller, "getBoundingClientRect")) ? function () { 102 return a()[d]; 103 } : function () { 104 return (isViewport ? _getViewportDimension(d2) : scroller["client" + d2]) || 0; 105 }; 106}, 107 _getOffsetsFunc = function _getOffsetsFunc(element, isViewport) { 108 return !isViewport || ~_proxies.indexOf(element) ? _getBoundsFunc(element) : function () { 109 return _winOffsets; 110 }; 111}, 112 _maxScroll = function _maxScroll(element, _ref2) { 113 var s = _ref2.s, 114 d2 = _ref2.d2, 115 d = _ref2.d, 116 a = _ref2.a; 117 return Math.max(0, (s = "scroll" + d2) && (a = _getProxyProp(element, s)) ? a() - _getBoundsFunc(element)()[d] : _isViewport(element) ? (_docEl[s] || _body[s]) - _getViewportDimension(d2) : element[s] - element["offset" + d2]); 118}, 119 _iterateAutoRefresh = function _iterateAutoRefresh(func, events) { 120 for (var i = 0; i < _autoRefresh.length; i += 3) {
vendor: 13,579 bytes, lines 121-494
121 (!events || ~events.indexOf(_autoRefresh[i + 1])) && func(_autoRefresh[i], _autoRefresh[i + 1], _autoRefresh[i + 2]); 122 } 123}, 124 _isString = function _isString(value) { 125 return typeof value === "string"; 126}, 127 _isFunction = function _isFunction(value) { 128 return typeof value === "function"; 129}, 130 _isNumber = function _isNumber(value) { 131 return typeof value === "number"; 132}, 133 _isObject = function _isObject(value) { 134 return typeof value === "object"; 135}, 136 _endAnimation = function _endAnimation(animation, reversed, pause) { 137 return animation && animation.progress(reversed ? 0 : 1) && pause && animation.pause(); 138}, 139 _callback = function _callback(self, func) { 140 if (self.enabled) { 141 var result = func(self); 142 result && result.totalTime && (self.callbackAnimation = result); 143 } 144}, 145 _abs = Math.abs, 146 _left = "left", 147 _top = "top", 148 _right = "right", 149 _bottom = "bottom", 150 _width = "width", 151 _height = "height", 152 _Right = "Right", 153 _Left = "Left", 154 _Top = "Top", 155 _Bottom = "Bottom", 156 _padding = "padding", 157 _margin = "margin", 158 _Width = "Width", 159 _Height = "Height", 160 _px = "px", 161 _getComputedStyle = function _getComputedStyle(element) { 162 return _win.getComputedStyle(element); 163}, 164 _makePositionable = function _makePositionable(element) { 165 // if the element already has position: absolute or fixed, leave that, otherwise make it position: relative 166 var position = _getComputedStyle(element).position; 167 168 element.style.position = position === "absolute" || position === "fixed" ? position : "relative"; 169}, 170 _setDefaults = function _setDefaults(obj, defaults) { 171 for (var p in defaults) { 172 p in obj || (obj[p] = defaults[p]); 173 } 174 175 return obj; 176}, 177 _getBounds = function _getBounds(element, withoutTransforms) { 178 var tween = withoutTransforms && _getComputedStyle(element)[_transformProp] !== "matrix(1, 0, 0, 1, 0, 0)" && gsap.to(element, { 179 x: 0, 180 y: 0, 181 xPercent: 0, 182 yPercent: 0, 183 rotation: 0, 184 rotationX: 0, 185 rotationY: 0, 186 scale: 1, 187 skewX: 0, 188 skewY: 0 189 }).progress(1), 190 bounds = element.getBoundingClientRect(); 191 tween && tween.progress(0).kill(); 192 return bounds; 193}, 194 _getSize = function _getSize(element, _ref3) { 195 var d2 = _ref3.d2; 196 return element["offset" + d2] || element["client" + d2] || 0; 197}, 198 _getLabelRatioArray = function _getLabelRatioArray(timeline) { 199 var a = [], 200 labels = timeline.labels, 201 duration = timeline.duration(), 202 p; 203 204 for (p in labels) { 205 a.push(labels[p] / duration); 206 } 207 208 return a; 209}, 210 _getClosestLabel = function _getClosestLabel(animation) { 211 return function (value) { 212 return gsap.utils.snap(_getLabelRatioArray(animation), value); 213 }; 214}, 215 _snapDirectional = function _snapDirectional(snapIncrementOrArray) { 216 var snap = gsap.utils.snap(snapIncrementOrArray), 217 a = Array.isArray(snapIncrementOrArray) && snapIncrementOrArray.slice(0).sort(function (a, b) { 218 return a - b; 219 }); 220 return a ? function (value, direction, threshold) { 221 if (threshold === void 0) { 222 threshold = 1e-3; 223 } 224 225 var i; 226 227 if (!direction) { 228 return snap(value); 229 } 230 231 if (direction > 0) { 232 value -= threshold; // to avoid rounding errors. If we're too strict, it might snap forward, then immediately again, and again. 233 234 for (i = 0; i < a.length; i++) { 235 if (a[i] >= value) { 236 return a[i]; 237 } 238 } 239 240 return a[i - 1]; 241 } else { 242 i = a.length; 243 value += threshold; 244 245 while (i--) { 246 if (a[i] <= value) { 247 return a[i]; 248 } 249 } 250 } 251 252 return a[0]; 253 } : function (value, direction, threshold) { 254 if (threshold === void 0) { 255 threshold = 1e-3; 256 } 257 258 var snapped = snap(value); 259 return !direction || Math.abs(snapped - value) < threshold || snapped - value < 0 === direction < 0 ? snapped : snap(direction < 0 ? value - snapIncrementOrArray : value + snapIncrementOrArray); 260 }; 261}, 262 _getLabelAtDirection = function _getLabelAtDirection(timeline) { 263 return function (value, st) { 264 return _snapDirectional(_getLabelRatioArray(timeline))(value, st.direction); 265 }; 266}, 267 _multiListener = function _multiListener(func, element, types, callback) { 268 return types.split(",").forEach(function (type) { 269 return func(element, type, callback); 270 }); 271}, 272 _addListener = function _addListener(element, type, func, nonPassive, capture) { 273 return element.addEventListener(type, func, { 274 passive: !nonPassive, 275 capture: !!capture 276 }); 277}, 278 _removeListener = function _removeListener(element, type, func, capture) { 279 return element.removeEventListener(type, func, !!capture); 280}, 281 _wheelListener = function _wheelListener(func, el, scrollFunc) { 282 scrollFunc = scrollFunc && scrollFunc.wheelHandler; 283 284 if (scrollFunc) { 285 func(el, "wheel", scrollFunc); 286 func(el, "touchmove", scrollFunc); 287 } 288}, 289 _markerDefaults = { 290 startColor: "green", 291 endColor: "red", 292 indent: 0, 293 fontSize: "16px", 294 fontWeight: "normal" 295}, 296 _defaults = { 297 toggleActions: "play", 298 anticipatePin: 0 299}, 300 _keywords = { 301 top: 0, 302 left: 0, 303 center: 0.5, 304 bottom: 1, 305 right: 1 306}, 307 _offsetToPx = function _offsetToPx(value, size) { 308 if (_isString(value)) { 309 var eqIndex = value.indexOf("="), 310 relative = ~eqIndex ? +(value.charAt(eqIndex - 1) + 1) * parseFloat(value.substr(eqIndex + 1)) : 0; 311 312 if (~eqIndex) { 313 value.indexOf("%") > eqIndex && (relative *= size / 100); 314 value = value.substr(0, eqIndex - 1); 315 } 316 317 value = relative + (value in _keywords ? _keywords[value] * size : ~value.indexOf("%") ? parseFloat(value) * size / 100 : parseFloat(value) || 0); 318 } 319 320 return value; 321}, 322 _createMarker = function _createMarker(type, name, container, direction, _ref4, offset, matchWidthEl, containerAnimation) { 323 var startColor = _ref4.startColor, 324 endColor = _ref4.endColor, 325 fontSize = _ref4.fontSize, 326 indent = _ref4.indent, 327 fontWeight = _ref4.fontWeight; 328 329 var e = _doc.createElement("div"), 330 useFixedPosition = _isViewport(container) || _getProxyProp(container, "pinType") === "fixed", 331 isScroller = type.indexOf("scroller") !== -1, 332 parent = useFixedPosition ? _body : container, 333 isStart = type.indexOf("start") !== -1, 334 color = isStart ? startColor : endColor, 335 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;"; 336 337 css += "position:" + ((isScroller || containerAnimation) && useFixedPosition ? "fixed;" : "absolute;"); 338 (isScroller || containerAnimation || !useFixedPosition) && (css += (direction === _vertical ? _right : _bottom) + ":" + (offset + parseFloat(indent)) + "px;"); 339 matchWidthEl && (css += "box-sizing:border-box;text-align:left;width:" + matchWidthEl.offsetWidth + "px;"); 340 e._isStart = isStart; 341 e.setAttribute("class", "gsap-marker-" + type + (name ? " marker-" + name : "")); 342 e.style.cssText = css; 343 e.innerText = name || name === 0 ? type + "-" + name : type; 344 parent.children[0] ? parent.insertBefore(e, parent.children[0]) : parent.appendChild(e); 345 e._offset = e["offset" + direction.op.d2]; 346 347 _positionMarker(e, 0, direction, isStart); 348 349 return e; 350}, 351 _positionMarker = function _positionMarker(marker, start, direction, flipped) { 352 var vars = { 353 display: "block" 354 }, 355 side = direction[flipped ? "os2" : "p2"], 356 oppositeSide = direction[flipped ? "p2" : "os2"]; 357 marker._isFlipped = flipped; 358 vars[direction.a + "Percent"] = flipped ? -100 : 0; 359 vars[direction.a] = flipped ? "1px" : 0; 360 vars["border" + side + _Width] = 1; 361 vars["border" + oppositeSide + _Width] = 0; 362 vars[direction.p] = start + "px"; 363 gsap.set(marker, vars); 364}, 365 _triggers = [], 366 _ids = {}, 367 _rafID, 368 _sync = function _sync() { 369 return _getTime() - _lastScrollTime > 34 && (_rafID || (_rafID = requestAnimationFrame(_updateAll))); 370}, 371 _onScroll = function _onScroll() { 372 // 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 373 if (!_normalizer || !_normalizer.isPressed || _normalizer.startX > _body.clientWidth) { 374 // if the user is dragging the scrollbar, allow it. 375 _scrollers.cache++; 376 377 if (_normalizer) { 378 _rafID || (_rafID = requestAnimationFrame(_updateAll)); 379 } else { 380 _updateAll(); // Safari in particular (on desktop) NEEDS the immediate update rather than waiting for a requestAnimationFrame() whereas iOS seems to benefit from waiting for the requestAnimationFrame() tick, at least when normalizing. See https://codepen.io/GreenSock/pen/qBYozqO?editors=0110 381 382 } 383 384 _lastScrollTime || _dispatch("scrollStart"); 385 _lastScrollTime = _getTime(); 386 } 387}, 388 _setBaseDimensions = function _setBaseDimensions() { 389 _baseScreenWidth = _win.innerWidth; 390 _baseScreenHeight = _win.innerHeight; 391}, 392 _onResize = function _onResize() { 393 _scrollers.cache++; 394 !_refreshing && !_ignoreResize && !_doc.fullscreenElement && !_doc.webkitFullscreenElement && (!_ignoreMobileResize || _baseScreenWidth !== _win.innerWidth || Math.abs(_win.innerHeight - _baseScreenHeight) > _win.innerHeight * 0.25) && _resizeDelay.restart(true); 395}, 396 // ignore resizes triggered by refresh() 397_listeners = {}, 398 _emptyArray = [], 399 _softRefresh = function _softRefresh() { 400 return _removeListener(ScrollTrigger, "scrollEnd", _softRefresh) || _refreshAll(true); 401}, 402 _dispatch = function _dispatch(type) { 403 return _listeners[type] && _listeners[type].map(function (f) { 404 return f(); 405 }) || _emptyArray; 406}, 407 _savedStyles = [], 408 // 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. 409_revertRecorded = function _revertRecorded(media) { 410 for (var i = 0; i < _savedStyles.length; i += 5) { 411 if (!media || _savedStyles[i + 4] && _savedStyles[i + 4].query === media) { 412 _savedStyles[i].style.cssText = _savedStyles[i + 1]; 413 _savedStyles[i].getBBox && _savedStyles[i].setAttribute("transform", _savedStyles[i + 2] || ""); 414 _savedStyles[i + 3].uncache = 1; 415 } 416 } 417}, 418 _revertAll = function _revertAll(kill, media) { 419 var trigger; 420 421 for (_i = 0; _i < _triggers.length; _i++) { 422 trigger = _triggers[_i]; 423 424 if (trigger && (!media || trigger._ctx === media)) { 425 if (kill) { 426 trigger.kill(1); 427 } else { 428 trigger.revert(true, true); 429 } 430 } 431 } 432 433 media && _revertRecorded(media); 434 media || _dispatch("revert"); 435}, 436 _clearScrollMemory = function _clearScrollMemory(scrollRestoration, force) { 437 // 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. 438 _scrollers.cache++; 439 (force || !_refreshingAll) && _scrollers.forEach(function (obj) { 440 return _isFunction(obj) && obj.cacheID++ && (obj.rec = 0); 441 }); 442 _isString(scrollRestoration) && (_win.history.scrollRestoration = _scrollRestoration = scrollRestoration); 443}, 444 _refreshingAll, 445 _refreshID = 0, 446 _queueRefreshID, 447 _queueRefreshAll = function _queueRefreshAll() { 448 // we don't want to call _refreshAll() every time we create a new ScrollTrigger (for performance reasons) - it's better to batch them. Some frameworks dynamically load content and we can't rely on the window's "load" or "DOMContentLoaded" events to trigger it. 449 if (_queueRefreshID !== _refreshID) { 450 var id = _queueRefreshID = _refreshID; 451 requestAnimationFrame(function () { 452 return id === _refreshID && _refreshAll(true); 453 }); 454 } 455}, 456 _refresh100vh = function _refresh100vh() { 457 _body.appendChild(_div100vh); 458 459 _100vh = _div100vh.offsetHeight || _win.innerHeight; 460 461 _body.removeChild(_div100vh); 462}, 463 _refreshAll = function _refreshAll(force, skipRevert) { 464 if (_lastScrollTime && !force) { 465 _addListener(ScrollTrigger, "scrollEnd", _softRefresh); 466 467 return; 468 } 469 470 _refresh100vh(); 471 472 _refreshingAll = ScrollTrigger.isRefreshing = true; 473 474 _scrollers.forEach(function (obj) { 475 return _isFunction(obj) && ++obj.cacheID && (obj.rec = obj()); 476 }); // force the clearing of the cache because some browsers take a little while to dispatch the "scroll" event and the user may have changed the scroll position and then called ScrollTrigger.refresh() right away 477 478 479 var refreshInits = _dispatch("refreshInit"); 480 481 _sort && ScrollTrigger.sort(); 482 skipRevert || _revertAll(); 483 484 _scrollers.forEach(function (obj) { 485 if (_isFunction(obj)) { 486 obj.smooth && (obj.target.style.scrollBehavior = "auto"); // smooth scrolling interferes 487 488 obj(0); 489 } 490 }); 491 492 _triggers.slice(0).forEach(function (t) { 493 return t.refresh(); 494 }); // don't loop with _i because during a refresh() someone could call ScrollTrigger.update(
vendor: 17,541 bytes, lines 494-951
494) which would iterate through _i resulting in a skip. 495 496 497 _triggers.forEach(function (t, i) { 498 // nested pins (pinnedContainer) with pinSpacing may expand the container, so we must accommodate that here. 499 if (t._subPinOffset && t.pin) { 500 var prop = t.vars.horizontal ? "offsetWidth" : "offsetHeight", 501 original = t.pin[prop]; 502 t.revert(true, 1); 503 t.adjustPinSpacing(t.pin[prop] - original); 504 t.refresh(); 505 } 506 }); 507 508 _triggers.forEach(function (t) { 509 // 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". Same for anything with a clamped end 510 var max = _maxScroll(t.scroller, t._dir); 511 512 (t.vars.end === "max" || t._endClamp && t.end > max) && t.setPositions(t.start, Math.max(t.start + 1, max), true); 513 }); 514 515 refreshInits.forEach(function (result) { 516 return result && result.render && result.render(-1); 517 }); // 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. 518 519 _scrollers.forEach(function (obj) { 520 if (_isFunction(obj)) { 521 obj.smooth && requestAnimationFrame(function () { 522 return obj.target.style.scrollBehavior = "smooth"; 523 }); 524 obj.rec && obj(obj.rec); 525 } 526 }); 527 528 _clearScrollMemory(_scrollRestoration, 1); 529 530 _resizeDelay.pause(); 531 532 _refreshID++; 533 _refreshingAll = 2; 534 535 _updateAll(2); 536 537 _triggers.forEach(function (t) { 538 return _isFunction(t.vars.onRefresh) && t.vars.onRefresh(t); 539 }); 540 541 _refreshingAll = ScrollTrigger.isRefreshing = false; 542 543 _dispatch("refresh"); 544}, 545 _lastScroll = 0, 546 _direction = 1, 547 _primary, 548 _updateAll = function _updateAll(force) { 549 if (!_refreshingAll || force === 2) { 550 ScrollTrigger.isUpdating = true; 551 _primary && _primary.update(0); // ScrollSmoother uses refreshPriority -9999 to become the primary that gets updated before all others because it affects the scroll position. 552 553 var l = _triggers.length, 554 time = _getTime(), 555 recordVelocity = time - _time1 >= 50, 556 scroll = l && _triggers[0].scroll(); 557 558 _direction = _lastScroll > scroll ? -1 : 1; 559 _refreshingAll || (_lastScroll = scroll); 560 561 if (recordVelocity) { 562 if (_lastScrollTime && !_pointerIsDown && time - _lastScrollTime > 200) { 563 _lastScrollTime = 0; 564 565 _dispatch("scrollEnd"); 566 } 567 568 _time2 = _time1; 569 _time1 = time; 570 } 571 572 if (_direction < 0) { 573 _i = l; 574 575 while (_i-- > 0) { 576 _triggers[_i] && _triggers[_i].update(0, recordVelocity); 577 } 578 579 _direction = 1; 580 } else { 581 for (_i = 0; _i < l; _i++) { 582 _triggers[_i] && _triggers[_i].update(0, recordVelocity); 583 } 584 } 585 586 ScrollTrigger.isUpdating = false; 587 } 588 589 _rafID = 0; 590}, 591 _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"], 592 _stateProps = _propNamesToCopy.concat([_width, _height, "boxSizing", "max" + _Width, "max" + _Height, "position", _margin, _padding, _padding + _Top, _padding + _Right, _padding + _Bottom, _padding + _Left]), 593 _swapPinOut = function _swapPinOut(pin, spacer, state) { 594 _setState(state); 595 596 var cache = pin._gsap; 597 598 if (cache.spacerIsNative) { 599 _setState(cache.spacerState); 600 } else if (pin._gsap.swappedIn) { 601 var parent = spacer.parentNode; 602 603 if (parent) { 604 parent.insertBefore(pin, spacer); 605 parent.removeChild(spacer); 606 } 607 } 608 609 pin._gsap.swappedIn = false; 610}, 611 _swapPinIn = function _swapPinIn(pin, spacer, cs, spacerState) { 612 if (!pin._gsap.swappedIn) { 613 var i = _propNamesToCopy.length, 614 spacerStyle = spacer.style, 615 pinStyle = pin.style, 616 p; 617 618 while (i--) { 619 p = _propNamesToCopy[i]; 620 spacerStyle[p] = cs[p]; 621 } 622 623 spacerStyle.position = cs.position === "absolute" ? "absolute" : "relative"; 624 cs.display === "inline" && (spacerStyle.display = "inline-block"); 625 pinStyle[_bottom] = pinStyle[_right] = "auto"; 626 spacerStyle.flexBasis = cs.flexBasis || "auto"; 627 spacerStyle.overflow = "visible"; 628 spacerStyle.boxSizing = "border-box"; 629 spacerStyle[_width] = _getSize(pin, _horizontal) + _px; 630 spacerStyle[_height] = _getSize(pin, _vertical) + _px; 631 spacerStyle[_padding] = pinStyle[_margin] = pinStyle[_top] = pinStyle[_left] = "0"; 632 633 _setState(spacerState); 634 635 pinStyle[_width] = pinStyle["max" + _Width] = cs[_width]; 636 pinStyle[_height] = pinStyle["max" + _Height] = cs[_height]; 637 pinStyle[_padding] = cs[_padding]; 638 639 if (pin.parentNode !== spacer) { 640 pin.parentNode.insertBefore(spacer, pin); 641 spacer.appendChild(pin); 642 } 643 644 pin._gsap.swappedIn = true; 645 } 646}, 647 _capsExp = /([A-Z])/g, 648 _setState = function _setState(state) { 649 if (state) { 650 var style = state.t.style, 651 l = state.length, 652 i = 0, 653 p, 654 value; 655 (state.t._gsap || gsap.core.getCache(state.t)).uncache = 1; // otherwise transforms may be off 656 657 for (; i < l; i += 2) { 658 value = state[i + 1]; 659 p = state[i]; 660 661 if (value) { 662 style[p] = value; 663 } else if (style[p]) { 664 style.removeProperty(p.replace(_capsExp, "-$1").toLowerCase()); 665 } 666 } 667 } 668}, 669 _getState = function _getState(element) { 670 // 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. 671 var l = _stateProps.length, 672 style = element.style, 673 state = [], 674 i = 0; 675 676 for (; i < l; i++) { 677 state.push(_stateProps[i], style[_stateProps[i]]); 678 } 679 680 state.t = element; 681 return state; 682}, 683 _copyState = function _copyState(state, override, omitOffsets) { 684 var result = [], 685 l = state.length, 686 i = omitOffsets ? 8 : 0, 687 // skip top, left, right, bottom if omitOffsets is true 688 p; 689 690 for (; i < l; i += 2) { 691 p = state[i]; 692 result.push(p, p in override ? override[p] : state[i + 1]); 693 } 694 695 result.t = state.t; 696 return result; 697}, 698 _winOffsets = { 699 left: 0, 700 top: 0 701}, 702 // // potential future feature (?) Allow users to calculate where a trigger hits (scroll position) like getScrollPosition("#id", "top bottom") 703// _getScrollPosition = (trigger, position, {scroller, containerAnimation, horizontal}) => { 704// scroller = _getTarget(scroller || _win); 705// let direction = horizontal ? _horizontal : _vertical, 706// isViewport = _isViewport(scroller); 707// _getSizeFunc(scroller, isViewport, direction); 708// 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); 709// }, 710_parsePosition = function _parsePosition(value, trigger, scrollerSize, direction, scroll, marker, markerScroller, self, scrollerBounds, borderWidth, useFixedPosition, scrollerMax, containerAnimation, clampZeroProp) { 711 _isFunction(value) && (value = value(self)); 712 713 if (_isString(value) && value.substr(0, 3) === "max") { 714 value = scrollerMax + (value.charAt(4) === "=" ? _offsetToPx("0" + value.substr(3), scrollerSize) : 0); 715 } 716 717 var time = containerAnimation ? containerAnimation.time() : 0, 718 p1, 719 p2, 720 element; 721 containerAnimation && containerAnimation.seek(0); 722 isNaN(value) || (value = +value); // convert a string number like "45" to an actual number 723 724 if (!_isNumber(value)) { 725 _isFunction(trigger) && (trigger = trigger(self)); 726 var offsets = (value || "0").split(" "), 727 bounds, 728 localOffset, 729 globalOffset, 730 display; 731 element = _getTarget(trigger, self) || _body; 732 bounds = _getBounds(element) || {}; 733 734 if ((!bounds || !bounds.left && !bounds.top) && _getComputedStyle(element).display === "none") { 735 // if display is "none", it won't report getBoundingClientRect() properly 736 display = element.style.display; 737 element.style.display = "block"; 738 bounds = _getBounds(element); 739 display ? element.style.display = display : element.style.removeProperty("display"); 740 } 741 742 localOffset = _offsetToPx(offsets[0], bounds[direction.d]); 743 globalOffset = _offsetToPx(offsets[1] || "0", scrollerSize); 744 value = bounds[direction.p] - scrollerBounds[direction.p] - borderWidth + localOffset + scroll - globalOffset; 745 markerScroller && _positionMarker(markerScroller, globalOffset, direction, scrollerSize - globalOffset < 20 || markerScroller._isStart && globalOffset > 20); 746 scrollerSize -= scrollerSize - globalOffset; // adjust for the marker 747 } else { 748 containerAnimation && (value = gsap.utils.mapRange(containerAnimation.scrollTrigger.start, containerAnimation.scrollTrigger.end, 0, scrollerMax, value)); 749 markerScroller && _positionMarker(markerScroller, scrollerSize, direction, true); 750 } 751 752 if (clampZeroProp) { 753 self[clampZeroProp] = value || -0.001; 754 value < 0 && (value = 0); 755 } 756 757 if (marker) { 758 var position = value + scrollerSize, 759 isStart = marker._isStart; 760 p1 = "scroll" + direction.d2; 761 762 _positionMarker(marker, position, direction, isStart && position > 20 || !isStart && (useFixedPosition ? Math.max(_body[p1], _docEl[p1]) : marker.parentNode[p1]) <= position + 1); 763 764 if (useFixedPosition) { 765 scrollerBounds = _getBounds(markerScroller); 766 useFixedPosition && (marker.style[direction.op.p] = scrollerBounds[direction.op.p] - direction.op.m - marker._offset + _px); 767 } 768 } 769 770 if (containerAnimation && element) { 771 p1 = _getBounds(element); 772 containerAnimation.seek(scrollerMax); 773 p2 = _getBounds(element); 774 containerAnimation._caScrollDist = p1[direction.p] - p2[direction.p]; 775 value = value / containerAnimation._caScrollDist * scrollerMax; 776 } 777 778 containerAnimation && containerAnimation.seek(time); 779 return containerAnimation ? value : Math.round(value); 780}, 781 _prefixExp = /(webkit|moz|length|cssText|inset)/i, 782 _reparent = function _reparent(element, parent, top, left) { 783 if (element.parentNode !== parent) { 784 var style = element.style, 785 p, 786 cs; 787 788 if (parent === _body) { 789 element._stOrig = style.cssText; // record original inline styles so we can revert them later 790 791 cs = _getComputedStyle(element); 792 793 for (p in cs) { 794 // must copy all relevant styles to ensure that nothing changes visually when we reparent to the <body>. Skip the vendor prefixed ones. 795 if (!+p && !_prefixExp.test(p) && cs[p] && typeof style[p] === "string" && p !== "0") { 796 style[p] = cs[p]; 797 } 798 } 799 800 style.top = top; 801 style.left = left; 802 } else { 803 style.cssText = element._stOrig; 804 } 805 806 gsap.core.getCache(element).uncache = 1; 807 parent.appendChild(element); 808 } 809}, 810 _interruptionTracker = function _interruptionTracker(getValueFunc, initialValue, onInterrupt) { 811 var last1 = initialValue, 812 last2 = last1; 813 return function (value) { 814 var current = Math.round(getValueFunc()); // round because in some [very uncommon] Windows environments, scroll can get reported with decimals even though it was set without. 815 816 if (current !== last1 && current !== last2 && Math.abs(current - last1) > 3 && Math.abs(current - last2) > 3) { 817 // 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. 818 value = current; 819 onInterrupt && onInterrupt(); 820 } 821 822 last2 = last1; 823 last1 = value; 824 return value; 825 }; 826}, 827 _shiftMarker = function _shiftMarker(marker, direction, value) { 828 var vars = {}; 829 vars[direction.p] = "+=" + value; 830 gsap.set(marker, vars); 831}, 832 // _mergeAnimations = animations => { 833// let tl = gsap.timeline({smoothChildTiming: true}).startTime(Math.min(...animations.map(a => a.globalTime(0)))); 834// animations.forEach(a => {let time = a.totalTime(); tl.add(a); a.totalTime(time); }); 835// tl.smoothChildTiming = false; 836// return tl; 837// }, 838// 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) 839_getTweenCreator = function _getTweenCreator(scroller, direction) { 840 var getScroll = _getScrollFunc(scroller, direction), 841 prop = "_scroll" + direction.p2, 842 // 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. 843 getTween = function getTween(scrollTo, vars, initialValue, change1, change2) { 844 var tween = getTween.tween, 845 onComplete = vars.onComplete, 846 modifiers = {}; 847 initialValue = initialValue || getScroll(); 848 849 var checkForInterruption = _interruptionTracker(getScroll, initialValue, function () { 850 tween.kill(); 851 getTween.tween = 0; 852 }); 853 854 change2 = change1 && change2 || 0; // if change1 is 0, we set that to the difference and ignore change2. Otherwise, there would be a compound effect. 855 856 change1 = change1 || scrollTo - initialValue; 857 tween && tween.kill(); 858 vars[prop] = scrollTo; 859 vars.modifiers = modifiers; 860 861 modifiers[prop] = function () { 862 return checkForInterruption(initialValue + change1 * tween.ratio + change2 * tween.ratio * tween.ratio); 863 }; 864 865 vars.onUpdate = function () { 866 _scrollers.cache++; 867 868 _updateAll(); 869 }; 870 871 vars.onComplete = function () { 872 getTween.tween = 0; 873 onComplete && onComplete.call(tween); 874 }; 875 876 tween = getTween.tween = gsap.to(scroller, vars); 877 return tween; 878 }; 879 880 scroller[prop] = getScroll; 881 882 getScroll.wheelHandler = function () { 883 return getTween.tween && getTween.tween.kill() && (getTween.tween = 0); 884 }; 885 886 _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. 887 888 889 ScrollTrigger.isTouch && _addListener(scroller, "touchmove", getScroll.wheelHandler); 890 return getTween; 891}; 892 893export var ScrollTrigger = /*#__PURE__*/function () { 894 function ScrollTrigger(vars, animation) { 895 _coreInitted || ScrollTrigger.register(gsap) || console.warn("Please gsap.registerPlugin(ScrollTrigger)"); 896 897 _context(this); 898 899 this.init(vars, animation); 900 } 901 902 var _proto = ScrollTrigger.prototype; 903 904 _proto.init = function init(vars, animation) { 905 this.progress = this.start = 0; 906 this.vars && this.kill(true, true); // in case it's being initted again 907 908 if (!_enabled) { 909 this.update = this.refresh = this.kill = _passThrough; 910 return; 911 } 912 913 vars = _setDefaults(_isString(vars) || _isNumber(vars) || vars.nodeType ? { 914 trigger: vars 915 } : vars, _defaults); 916 917 var _vars = vars, 918 onUpdate = _vars.onUpdate, 919 toggleClass = _vars.toggleClass, 920 id = _vars.id, 921 onToggle = _vars.onToggle, 922 onRefresh = _vars.onRefresh, 923 scrub = _vars.scrub, 924 trigger = _vars.trigger, 925 pin = _vars.pin, 926 pinSpacing = _vars.pinSpacing, 927 invalidateOnRefresh = _vars.invalidateOnRefresh, 928 anticipatePin = _vars.anticipatePin, 929 onScrubComplete = _vars.onScrubComplete, 930 onSnapComplete = _vars.onSnapComplete, 931 once = _vars.once, 932 snap = _vars.snap, 933 pinReparent = _vars.pinReparent, 934 pinSpacer = _vars.pinSpacer, 935 containerAnimation = _vars.containerAnimation, 936 fastScrollEnd = _vars.fastScrollEnd, 937 preventOverlaps = _vars.preventOverlaps, 938 direction = vars.horizontal || vars.containerAnimation && vars.horizontal !== false ? _horizontal : _vertical, 939 isToggle = !scrub && scrub !== 0, 940 scroller = _getTarget(vars.scroller || _win), 941 scrollerCache = gsap.core.getCache(scroller), 942 isViewport = _isViewport(scroller), 943 useFixedPosition = ("pinType" in vars ? vars.pinType : _getProxyProp(scroller, "pinType") || isViewport && "fixed") === "fixed", 944 callbacks = [vars.onEnter, vars.onLeave, vars.onEnterBack, vars.onLeaveBack], 945 toggleActions = isToggle && vars.toggleActions.split(" "), 946 markers = "markers" in vars ? vars.markers : _defaults.markers, 947 borderWidth = isViewport ? 0 : parseFloat(_getComputedStyle(scroller)["border" + direction.p2 + _Width]) || 0, 948 self = this, 949 onRefreshInit = vars.onRefreshInit && function () { 950 return vars.onRefreshInit(self); 951 },
vendor: 4,094 bytes, lines 952-1060
952 getScrollerSize = _getSizeFunc(scroller, isViewport, direction), 953 getScrollerOffsets = _getOffsetsFunc(scroller, isViewport), 954 lastSnap = 0, 955 lastRefresh = 0, 956 prevProgress = 0, 957 scrollFunc = _getScrollFunc(scroller, direction), 958 tweenTo, 959 pinCache, 960 snapFunc, 961 scroll1, 962 scroll2, 963 start, 964 end, 965 markerStart, 966 markerEnd, 967 markerStartTrigger, 968 markerEndTrigger, 969 markerVars, 970 executingOnRefresh, 971 change, 972 pinOriginalState, 973 pinActiveState, 974 pinState, 975 spacer, 976 offset, 977 pinGetter, 978 pinSetter, 979 pinStart, 980 pinChange, 981 spacingStart, 982 spacerState, 983 markerStartSetter, 984 pinMoves, 985 markerEndSetter, 986 cs, 987 snap1, 988 snap2, 989 scrubTween, 990 scrubSmooth, 991 snapDurClamp, 992 snapDelayedCall, 993 prevScroll, 994 prevAnimProgress, 995 caMarkerSetter, 996 customRevertReturn; // for the sake of efficiency, _startClamp/_endClamp serve like a truthy value indicating that clamping was enabled on the start/end, and ALSO store the actual pre-clamped numeric value. We tap into that in ScrollSmoother for speed effects. So for example, if start="clamp(top bottom)" results in a start of -100 naturally, it would get clamped to 0 but -100 would be stored in _startClamp. 997 998 999 self._startClamp = self._endClamp = false; 1000 self._dir = direction; 1001 anticipatePin *= 45; 1002 self.scroller = scroller; 1003 self.scroll = containerAnimation ? containerAnimation.time.bind(containerAnimation) : scrollFunc; 1004 scroll1 = scrollFunc(); 1005 self.vars = vars; 1006 animation = animation || vars.animation; 1007 1008 if ("refreshPriority" in vars) { 1009 _sort = 1; 1010 vars.refreshPriority === -9999 && (_primary = self); // used by ScrollSmoother 1011 } 1012 1013 scrollerCache.tweenScroll = scrollerCache.tweenScroll || { 1014 top: _getTweenCreator(scroller, _vertical), 1015 left: _getTweenCreator(scroller, _horizontal) 1016 }; 1017 self.tweenTo = tweenTo = scrollerCache.tweenScroll[direction.p]; 1018 1019 self.scrubDuration = function (value) { 1020 scrubSmooth = _isNumber(value) && value; 1021 1022 if (!scrubSmooth) { 1023 scrubTween && scrubTween.progress(1).kill(); 1024 scrubTween = 0; 1025 } else { 1026 scrubTween ? scrubTween.duration(value) : scrubTween = gsap.to(animation, { 1027 ease: "expo", 1028 totalProgress: "+=0", 1029 duration: scrubSmooth, 1030 paused: true, 1031 onComplete: function onComplete() { 1032 return onScrubComplete && onScrubComplete(self); 1033 } 1034 }); 1035 } 1036 }; 1037 1038 if (animation) { 1039 animation.vars.lazy = false; 1040 animation._initted && !self.isReverted || animation.vars.immediateRender !== false && vars.immediateRender !== false && animation.duration() && animation.render(0, true, true); // special case: if this ScrollTrigger gets re-initted, a from() tween with a stagger could get initted initially and then reverted on the re-init which means it'll need to get rendered again here to properly display things. Otherwise, See https://greensock.com/forums/topic/36777-scrollsmoother-splittext-nextjs/ and https://codepen.io/GreenSock/pen/eYPyPpd?editors=0010 1041 1042 self.animation = animation.pause(); 1043 animation.scrollTrigger = self; 1044 self.scrubDuration(scrub); 1045 snap1 = 0; 1046 id || (id = animation.vars.id); 1047 } 1048 1049 if (snap) { 1050 // TODO: potential idea: use legitimate CSS scroll snapping by pushing invisible elements into the DOM that serve as snap positions, and toggle the document.scrollingElement.style.scrollSnapType onToggle. See https://codepen.io/GreenSock/pen/JjLrgWM for a quick proof of concept. 1051 if (!_isObject(snap) || snap.push) { 1052 snap = { 1053 snapTo: snap 1054 }; 1055 } 1056 1057 "scrollBehavior" in _body.style && gsap.set(isViewport ? [_body, _docEl] : scroller, { 1058 scrollBehavior: "auto" 1059 }); // smooth scrolling doesn't work with snap. 1060
vendor: 2,718 bytes, lines 1061-1106
1061 _scrollers.forEach(function (o) { 1062 return _isFunction(o) && o.target === (isViewport ? _doc.scrollingElement || _docEl : scroller) && (o.smooth = false); 1063 }); // note: set smooth to false on both the vertical and horizontal scroll getters/setters 1064 1065 1066 snapFunc = _isFunction(snap.snapTo) ? snap.snapTo : snap.snapTo === "labels" ? _getClosestLabel(animation) : snap.snapTo === "labelsDirectional" ? _getLabelAtDirection(animation) : snap.directional !== false ? function (value, st) { 1067 return _snapDirectional(snap.snapTo)(value, _getTime() - lastRefresh < 500 ? 0 : st.direction); 1068 } : gsap.utils.snap(snap.snapTo); 1069 snapDurClamp = snap.duration || { 1070 min: 0.1, 1071 max: 2 1072 }; 1073 snapDurClamp = _isObject(snapDurClamp) ? _clamp(snapDurClamp.min, snapDurClamp.max) : _clamp(snapDurClamp, snapDurClamp); 1074 snapDelayedCall = gsap.delayedCall(snap.delay || scrubSmooth / 2 || 0.1, function () { 1075 var scroll = scrollFunc(), 1076 refreshedRecently = _getTime() - lastRefresh < 500, 1077 tween = tweenTo.tween; 1078 1079 if ((refreshedRecently || Math.abs(self.getVelocity()) < 10) && !tween && !_pointerIsDown && lastSnap !== scroll) { 1080 var progress = (scroll - start) / change, 1081 totalProgress = animation && !isToggle ? animation.totalProgress() : progress, 1082 velocity = refreshedRecently ? 0 : (totalProgress - snap2) / (_getTime() - _time2) * 1000 || 0, 1083 change1 = gsap.utils.clamp(-progress, 1 - progress, _abs(velocity / 2) * velocity / 0.185), 1084 naturalEnd = progress + (snap.inertia === false ? 0 : change1), 1085 endValue = _clamp(0, 1, snapFunc(naturalEnd, self)), 1086 endScroll = Math.round(start + endValue * change), 1087 _snap = snap, 1088 onStart = _snap.onStart, 1089 _onInterrupt = _snap.onInterrupt, 1090 _onComplete = _snap.onComplete; 1091 1092 if (scroll <= end && scroll >= start && endScroll !== scroll) { 1093 if (tween && !tween._initted && tween.data <= _abs(endScroll - scroll)) { 1094 // there's an overlapping snap! So we must figure out which one is closer and let that tween live. 1095 return; 1096 } 1097 1098 if (snap.inertia === false) { 1099 change1 = endValue - progress; 1100 } 1101 1102 tweenTo(endScroll, { 1103 duration: snapDurClamp(_abs(Math.max(_abs(naturalEnd - totalProgress), _abs(endValue - totalProgress)) * 0.185 / velocity / 0.05 || 0)), 1104 ease: snap.ease || "power3", 1105 data: _abs(endScroll - scroll), 1106 // record the distance so that if another snap tween occurs (conflict) we
vendor: 5,075 bytes, lines 1106-1198
1106can prioritize the closest snap. 1107 onInterrupt: function onInterrupt() { 1108 return snapDelayedCall.restart(true) && _onInterrupt && _onInterrupt(self); 1109 }, 1110 onComplete: function onComplete() { 1111 self.update(); 1112 lastSnap = scrollFunc(); 1113 snap1 = snap2 = animation && !isToggle ? animation.totalProgress() : self.progress; 1114 onSnapComplete && onSnapComplete(self); 1115 _onComplete && _onComplete(self); 1116 } 1117 }, scroll, change1 * change, endScroll - scroll - change1 * change); 1118 onStart && onStart(self, tweenTo.tween); 1119 } 1120 } else if (self.isActive && lastSnap !== scroll) { 1121 snapDelayedCall.restart(true); 1122 } 1123 }).pause(); 1124 } 1125 1126 id && (_ids[id] = self); 1127 trigger = self.trigger = _getTarget(trigger || pin !== true && 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. 1128 1129 customRevertReturn = trigger && trigger._gsap && trigger._gsap.stRevert; 1130 customRevertReturn && (customRevertReturn = customRevertReturn(self)); 1131 pin = pin === true ? trigger : _getTarget(pin); 1132 _isString(toggleClass) && (toggleClass = { 1133 targets: trigger, 1134 className: toggleClass 1135 }); 1136 1137 if (pin) { 1138 pinSpacing === false || pinSpacing === _margin || (pinSpacing = !pinSpacing && pin.parentNode && pin.parentNode.style && _getComputedStyle(pin.parentNode).display === "flex" ? false : _padding); // if the parent is display: flex, don't apply pinSpacing by default. We should check that pin.parentNode is an element (not shadow dom window) 1139 1140 self.pin = pin; 1141 pinCache = gsap.core.getCache(pin); 1142 1143 if (!pinCache.spacer) { 1144 // 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. 1145 if (pinSpacer) { 1146 pinSpacer = _getTarget(pinSpacer); 1147 pinSpacer && !pinSpacer.nodeType && (pinSpacer = pinSpacer.current || pinSpacer.nativeElement); // for React & Angular 1148 1149 pinCache.spacerIsNative = !!pinSpacer; 1150 pinSpacer && (pinCache.spacerState = _getState(pinSpacer)); 1151 } 1152 1153 pinCache.spacer = spacer = pinSpacer || _doc.createElement("div"); 1154 spacer.classList.add("pin-spacer"); 1155 id && spacer.classList.add("pin-spacer-" + id); 1156 pinCache.pinState = pinOriginalState = _getState(pin); 1157 } else { 1158 pinOriginalState = pinCache.pinState; 1159 } 1160 1161 vars.force3D !== false && gsap.set(pin, { 1162 force3D: true 1163 }); 1164 self.spacer = spacer = pinCache.spacer; 1165 cs = _getComputedStyle(pin); 1166 spacingStart = cs[pinSpacing + direction.os2]; 1167 pinGetter = gsap.getProperty(pin); 1168 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). 1169 1170 _swapPinIn(pin, spacer, cs); 1171 1172 pinState = _getState(pin); 1173 } 1174 1175 if (markers) { 1176 markerVars = _isObject(markers) ? _setDefaults(markers, _markerDefaults) : _markerDefaults; 1177 markerStartTrigger = _createMarker("scroller-start", id, scroller, direction, markerVars, 0); 1178 markerEndTrigger = _createMarker("scroller-end", id, scroller, direction, markerVars, 0, markerStartTrigger); 1179 offset = markerStartTrigger["offset" + direction.op.d2]; 1180 1181 var content = _getTarget(_getProxyProp(scroller, "content") || scroller); 1182 1183 markerStart = this.markerStart = _createMarker("start", id, content, direction, markerVars, offset, 0, containerAnimation); 1184 markerEnd = this.markerEnd = _createMarker("end", id, content, direction, markerVars, offset, 0, containerAnimation); 1185 containerAnimation && (caMarkerSetter = gsap.quickSetter([markerStart, markerEnd], direction.a, _px)); 1186 1187 if (!useFixedPosition && !(_proxies.length && _getProxyProp(scroller, "fixedMarkers") === true)) { 1188 _makePositionable(isViewport ? _body : scroller); 1189 1190 gsap.set([markerStartTrigger, markerEndTrigger], { 1191 force3D: true 1192 }); 1193 markerStartSetter = gsap.quickSetter(markerStartTrigger, direction.a, _px); 1194 markerEndSetter = gsap.quickSetter(markerEndTrigger, direction.a, _px); 1195 } 1196 } 1197 1198 if (containerAnimation) {
vendor: 7,204 bytes, lines 1199-1348
1199 var oldOnUpdate = containerAnimation.vars.onUpdate, 1200 oldParams = containerAnimation.vars.onUpdateParams; 1201 containerAnimation.eventCallback("onUpdate", function () { 1202 self.update(0, 0, 1); 1203 oldOnUpdate && oldOnUpdate.apply(containerAnimation, oldParams || []); 1204 }); 1205 } 1206 1207 self.previous = function () { 1208 return _triggers[_triggers.indexOf(self) - 1]; 1209 }; 1210 1211 self.next = function () { 1212 return _triggers[_triggers.indexOf(self) + 1]; 1213 }; 1214 1215 self.revert = function (revert, temp) { 1216 if (!temp) { 1217 return self.kill(true); 1218 } // for compatibility with gsap.context() and gsap.matchMedia() which call revert() 1219 1220 1221 var r = revert !== false || !self.enabled, 1222 prevRefreshing = _refreshing; 1223 1224 if (r !== self.isReverted) { 1225 if (r) { 1226 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. 1227 1228 prevProgress = self.progress; 1229 prevAnimProgress = animation && animation.progress(); 1230 } 1231 1232 markerStart && [markerStart, markerEnd, markerStartTrigger, markerEndTrigger].forEach(function (m) { 1233 return m.style.display = r ? "none" : "block"; 1234 }); 1235 1236 if (r) { 1237 _refreshing = self; 1238 self.update(r); // make sure the pin is back in its original position so that all the measurements are correct. do this BEFORE swapping the pin out 1239 } 1240 1241 if (pin && (!pinReparent || !self.isActive)) { 1242 if (r) { 1243 _swapPinOut(pin, spacer, pinOriginalState); 1244 } else { 1245 _swapPinIn(pin, spacer, _getComputedStyle(pin), spacerState); 1246 } 1247 } 1248 1249 r || self.update(r); // when we're restoring, the update should run AFTER swapping the pin into its pin-spacer. 1250 1251 _refreshing = prevRefreshing; // restore. We set it to true during the update() so that things fire properly in there. 1252 1253 self.isReverted = r; 1254 } 1255 }; 1256 1257 self.refresh = function (soft, force, position, pinOffset) { 1258 // position is typically only defined if it's coming from setPositions() - it's a way to skip the normal parsing. pinOffset is also only from setPositions() and is mostly related to fancy stuff we need to do in ScrollSmoother with effects 1259 if ((_refreshing || !self.enabled) && !force) { 1260 return; 1261 } 1262 1263 if (pin && soft && _lastScrollTime) { 1264 _addListener(ScrollTrigger, "scrollEnd", _softRefresh); 1265 1266 return; 1267 } 1268 1269 !_refreshingAll && onRefreshInit && onRefreshInit(self); 1270 _refreshing = self; 1271 1272 if (tweenTo.tween && !position) { 1273 // we skip this if a position is passed in because typically that's from .setPositions() and it's best to allow in-progress snapping to continue. 1274 tweenTo.tween.kill(); 1275 tweenTo.tween = 0; 1276 } 1277 1278 scrubTween && scrubTween.pause(); 1279 invalidateOnRefresh && animation && animation.revert({ 1280 kill: false 1281 }).invalidate(); 1282 self.isReverted || self.revert(true, true); 1283 self._subPinOffset = false; // we'll set this to true in the sub-pins if we find any 1284 1285 var size = getScrollerSize(), 1286 scrollerBounds = getScrollerOffsets(), 1287 max = containerAnimation ? containerAnimation.duration() : _maxScroll(scroller, direction), 1288 isFirstRefresh = change <= 0.01, 1289 offset = 0, 1290 otherPinOffset = pinOffset || 0, 1291 parsedEnd = _isObject(position) ? position.end : vars.end, 1292 parsedEndTrigger = vars.endTrigger || trigger, 1293 parsedStart = _isObject(position) ? position.start : vars.start || (vars.start === 0 || !trigger ? 0 : pin ? "0 0" : "0 100%"), 1294 pinnedContainer = self.pinnedContainer = vars.pinnedContainer && _getTarget(vars.pinnedContainer, self), 1295 triggerIndex = trigger && Math.max(0, _triggers.indexOf(self)) || 0, 1296 i = triggerIndex, 1297 cs, 1298 bounds, 1299 scroll, 1300 isVertical, 1301 override, 1302 curTrigger, 1303 curPin, 1304 oppositeScroll, 1305 initted, 1306 revertedPins, 1307 forcedOverflow, 1308 markerStartOffset, 1309 markerEndOffset; 1310 1311 if (markers && _isObject(position)) { 1312 // if we alter the start/end positions with .setPositions(), it generally feeds in absolute NUMBERS which don't convey information about where to line up the markers, so to keep it intuitive, we record how far the trigger positions shift after applying the new numbers and then offset by that much in the opposite direction. We do the same to the associated trigger markers too of course. 1313 markerStartOffset = gsap.getProperty(markerStartTrigger, direction.p); 1314 markerEndOffset = gsap.getProperty(markerEndTrigger, direction.p); 1315 } 1316 1317 while (i--) { 1318 // 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. 1319 curTrigger = _triggers[i]; 1320 curTrigger.end || curTrigger.refresh(0, 1) || (_refreshing = self); // 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. 1321 1322 curPin = curTrigger.pin; 1323 1324 if (curPin && (curPin === trigger || curPin === pin || curPin === pinnedContainer) && !curTrigger.isReverted) { 1325 revertedPins || (revertedPins = []); 1326 revertedPins.unshift(curTrigger); // we'll revert from first to last to make sure things reach their end state properly 1327 1328 curTrigger.revert(true, true); 1329 } 1330 1331 if (curTrigger !== _triggers[i]) { 1332 // in case it got removed. 1333 triggerIndex--; 1334 i--; 1335 } 1336 } 1337 1338 _isFunction(parsedStart) && (parsedStart = parsedStart(self)); 1339 parsedStart = _parseClamp(parsedStart, "start", self); 1340 start = _parsePosition(parsedStart, trigger, size, direction, scrollFunc(), markerStart, markerStartTrigger, self, scrollerBounds, borderWidth, useFixedPosition, max, containerAnimation, self._startClamp && "_startClamp") || (pin ? -0.001 : 0); 1341 _isFunction(parsedEnd) && (parsedEnd = parsedEnd(self)); 1342 1343 if (_isString(parsedEnd) && !parsedEnd.indexOf("+=")) { 1344 if (~parsedEnd.indexOf(" ")) { 1345 parsedEnd = (_isString(parsedStart) ? parsedStart.split(" ")[0] : "") + parsedEnd; 1346 } else { 1347 offset = _offsetToPx(parsedEnd.substr(2), size); 1348 parsedEnd = _isString(parsedStart) ? parsedStart : (containerAnimation ? gsap.utils.mapRange(0, containerAnimation.duration(), containerAnimation.scrollTrigger.start, containerAnimation.scrollTrigger.end, start) : start) + offset; // _parsePosition won't factor in the offset if the start is a
vendor: 4,317 bytes, lines 1348-1438
1348number, so do it here. 1349 1350 parsedEndTrigger = trigger; 1351 } 1352 } 1353 1354 parsedEnd = _parseClamp(parsedEnd, "end", self); 1355 end = Math.max(start, _parsePosition(parsedEnd || (parsedEndTrigger ? "100% 0" : max), parsedEndTrigger, size, direction, scrollFunc() + offset, markerEnd, markerEndTrigger, self, scrollerBounds, borderWidth, useFixedPosition, max, containerAnimation, self._endClamp && "_endClamp")) || -0.001; 1356 offset = 0; 1357 i = triggerIndex; 1358 1359 while (i--) { 1360 curTrigger = _triggers[i]; 1361 curPin = curTrigger.pin; 1362 1363 if (curPin && curTrigger.start - curTrigger._pinPush <= start && !containerAnimation && curTrigger.end > 0) { 1364 cs = curTrigger.end - (self._startClamp ? Math.max(0, curTrigger.start) : curTrigger.start); 1365 1366 if ((curPin === trigger && curTrigger.start - curTrigger._pinPush < start || curPin === pinnedContainer) && isNaN(parsedStart)) { 1367 // numeric start values shouldn't be offset at all - treat them as absolute 1368 offset += cs * (1 - curTrigger.progress); 1369 } 1370 1371 curPin === pin && (otherPinOffset += cs); 1372 } 1373 } 1374 1375 start += offset; 1376 end += offset; 1377 self._startClamp && (self._startClamp += offset); 1378 1379 if (self._endClamp && !_refreshingAll) { 1380 self._endClamp = end || -0.001; 1381 end = Math.min(end, _maxScroll(scroller, direction)); 1382 } 1383 1384 change = end - start || (start -= 0.01) && 0.001; 1385 1386 if (isFirstRefresh) { 1387 // on the very first refresh(), the prevProgress couldn't have been accurate yet because the start/end were never calculated, so we set it here. Before 3.11.5, it could lead to an inaccurate scroll position restoration with snapping. 1388 prevProgress = gsap.utils.clamp(0, 1, gsap.utils.normalize(start, end, prevScroll)); 1389 } 1390 1391 self._pinPush = otherPinOffset; 1392 1393 if (markerStart && offset) { 1394 // offset the markers if necessary 1395 cs = {}; 1396 cs[direction.a] = "+=" + offset; 1397 pinnedContainer && (cs[direction.p] = "-=" + scrollFunc()); 1398 gsap.set([markerStart, markerEnd], cs); 1399 } 1400 1401 if (pin) { 1402 cs = _getComputedStyle(pin); 1403 isVertical = direction === _vertical; 1404 scroll = scrollFunc(); // recalculate because the triggers can affect the scroll 1405 1406 pinStart = parseFloat(pinGetter(direction.a)) + otherPinOffset; 1407 1408 if (!max && end > 1) { 1409 // 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/ 1410 forcedOverflow = (isViewport ? _doc.scrollingElement || _docEl : scroller).style; 1411 forcedOverflow = { 1412 style: forcedOverflow, 1413 value: forcedOverflow["overflow" + direction.a.toUpperCase()] 1414 }; 1415 1416 if (isViewport && _getComputedStyle(_body)["overflow" + direction.a.toUpperCase()] !== "scroll") { 1417 // avoid an extra scrollbar if BOTH <html> and <body> have overflow set to "scroll" 1418 forcedOverflow.style["overflow" + direction.a.toUpperCase()] = "scroll"; 1419 } 1420 } 1421 1422 _swapPinIn(pin, spacer, cs); 1423 1424 pinState = _getState(pin); // transforms will interfere with the top/left/right/bottom placement, so remove them temporarily. getBoundingClientRect() factors in transforms. 1425 1426 bounds = _getBounds(pin, true); 1427 oppositeScroll = useFixedPosition && _getScrollFunc(scroller, isVertical ? _horizontal : _vertical)(); 1428 1429 if (pinSpacing) { 1430 spacerState = [pinSpacing + direction.os2, change + otherPinOffset + _px]; 1431 spacerState.t = spacer; 1432 i = pinSpacing === _padding ? _getSize(pin, direction) + change + otherPinOffset : 0; 1433 i && spacerState.push(direction.d, i + _px); // for box-sizing: border-box (must include padding). 1434 1435 _setState(spacerState); 1436 1437 if (pinnedContainer) { 1438 // in ScrollTrigger.refresh(), we need to re-evaluate the pinContainer's size because this pinSpacing may stretch it out, but we can't just add the exact distance because depending on layout, it may not push things down or it may only do so partially.
vendor: 5,553 bytes, lines 1439-1537
1439 _triggers.forEach(function (t) { 1440 if (t.pin === pinnedContainer && t.vars.pinSpacing !== false) { 1441 t._subPinOffset = true; 1442 } 1443 }); 1444 } 1445 1446 useFixedPosition && scrollFunc(prevScroll); 1447 } 1448 1449 if (useFixedPosition) { 1450 override = { 1451 top: bounds.top + (isVertical ? scroll - start : oppositeScroll) + _px, 1452 left: bounds.left + (isVertical ? oppositeScroll : scroll - start) + _px, 1453 boxSizing: "border-box", 1454 position: "fixed" 1455 }; 1456 override[_width] = override["max" + _Width] = Math.ceil(bounds.width) + _px; 1457 override[_height] = override["max" + _Height] = Math.ceil(bounds.height) + _px; 1458 override[_margin] = override[_margin + _Top] = override[_margin + _Right] = override[_margin + _Bottom] = override[_margin + _Left] = "0"; 1459 override[_padding] = cs[_padding]; 1460 override[_padding + _Top] = cs[_padding + _Top]; 1461 override[_padding + _Right] = cs[_padding + _Right]; 1462 override[_padding + _Bottom] = cs[_padding + _Bottom]; 1463 override[_padding + _Left] = cs[_padding + _Left]; 1464 pinActiveState = _copyState(pinOriginalState, override, pinReparent); 1465 _refreshingAll && scrollFunc(0); 1466 } 1467 1468 if (animation) { 1469 // 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. 1470 initted = animation._initted; // if not, we must invalidate() after this step, otherwise it could lock in starting values prematurely. 1471 1472 _suppressOverwrites(1); 1473 1474 animation.render(animation.duration(), true, true); 1475 pinChange = pinGetter(direction.a) - pinStart + change + otherPinOffset; 1476 pinMoves = Math.abs(change - pinChange) > 1; 1477 useFixedPosition && pinMoves && 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. 1478 1479 animation.render(0, true, true); 1480 initted || animation.invalidate(true); 1481 animation.parent || animation.totalTime(animation.totalTime()); // if, for example, a toggleAction called play() and then refresh() happens and when we render(1) above, it would cause the animation to complete and get removed from its parent, so this makes sure it gets put back in. 1482 1483 _suppressOverwrites(0); 1484 } else { 1485 pinChange = change; 1486 } 1487 1488 forcedOverflow && (forcedOverflow.value ? forcedOverflow.style["overflow" + direction.a.toUpperCase()] = forcedOverflow.value : forcedOverflow.style.removeProperty("overflow-" + direction.a)); 1489 } else if (trigger && scrollFunc() && !containerAnimation) { 1490 // 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() 1491 bounds = trigger.parentNode; 1492 1493 while (bounds && bounds !== _body) { 1494 if (bounds._pinOffset) { 1495 start -= bounds._pinOffset; 1496 end -= bounds._pinOffset; 1497 } 1498 1499 bounds = bounds.parentNode; 1500 } 1501 } 1502 1503 revertedPins && revertedPins.forEach(function (t) { 1504 return t.revert(false, true); 1505 }); 1506 self.start = start; 1507 self.end = end; 1508 scroll1 = scroll2 = _refreshingAll ? prevScroll : scrollFunc(); // reset velocity 1509 1510 if (!containerAnimation && !_refreshingAll) { 1511 scroll1 < prevScroll && scrollFunc(prevScroll); 1512 self.scroll.rec = 0; 1513 } 1514 1515 self.revert(false, true); 1516 lastRefresh = _getTime(); 1517 1518 if (snapDelayedCall) { 1519 lastSnap = -1; // just so snapping gets re-enabled, clear out any recorded last value 1520 // self.isActive && scrollFunc(start + change * prevProgress); // previously this line was here to ensure that when snapping kicks in, it's from the previous progress but in some cases that's not desirable, like an all-page ScrollTrigger when new content gets added to the page, that'd totally change the progress. 1521 1522 snapDelayedCall.restart(true); 1523 } 1524 1525 _refreshing = 0; 1526 animation && isToggle && (animation._initted || prevAnimProgress) && animation.progress() !== prevAnimProgress && animation.progress(prevAnimProgress || 0, 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(). 1527 1528 if (isFirstRefresh || prevProgress !== self.progress || containerAnimation) { 1529 // 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. 1530 animation && !isToggle && animation.totalProgress(containerAnimation && start < -0.001 && !prevProgress ? gsap.utils.normalize(start, end, 0) : prevProgress, true); // to avoid issues where animation callbacks like onStart aren't triggered. 1531 1532 self.progress = isFirstRefresh || (scroll1 - start) / change === prevProgress ? 0 : prevProgress; 1533 } 1534 1535 pin && pinSpacing && (spacer._pinOffset = Math.round(self.progress * pinChange)); 1536 scrubTween && scrubTween.invalidate(); 1537
vendor: 4,238 bytes, lines 1538-1623
1538 if (!isNaN(markerStartOffset)) { 1539 // numbers were passed in for the position which are absolute, so instead of just putting the markers at the very bottom of the viewport, we figure out how far they shifted down (it's safe to assume they were originally positioned in closer relation to the trigger element with values like "top", "center", a percentage or whatever, so we offset that much in the opposite direction to basically revert them to the relative position thy were at previously. 1540 markerStartOffset -= gsap.getProperty(markerStartTrigger, direction.p); 1541 markerEndOffset -= gsap.getProperty(markerEndTrigger, direction.p); 1542 1543 _shiftMarker(markerStartTrigger, direction, markerStartOffset); 1544 1545 _shiftMarker(markerStart, direction, markerStartOffset - (pinOffset || 0)); 1546 1547 _shiftMarker(markerEndTrigger, direction, markerEndOffset); 1548 1549 _shiftMarker(markerEnd, direction, markerEndOffset - (pinOffset || 0)); 1550 } 1551 1552 isFirstRefresh && !_refreshingAll && self.update(); // edge case - when you reload a page when it's already scrolled down, some browsers fire a "scroll" event before DOMContentLoaded, triggering an updateAll(). If we don't update the self.progress as part of refresh(), then when it happens next, it may record prevProgress as 0 when it really shouldn't, potentially causing a callback in an animation to fire again. 1553 1554 if (onRefresh && !_refreshingAll && !executingOnRefresh) { 1555 // when refreshing all, we do extra work to correct pinnedContainer sizes and ensure things don't exceed the maxScroll, so we should do all the refreshes at the end after all that work so that the start/end values are corrected. 1556 executingOnRefresh = true; 1557 onRefresh(self); 1558 executingOnRefresh = false; 1559 } 1560 }; 1561 1562 self.getVelocity = function () { 1563 return (scrollFunc() - scroll2) / (_getTime() - _time2) * 1000 || 0; 1564 }; 1565 1566 self.endAnimation = function () { 1567 _endAnimation(self.callbackAnimation); 1568 1569 if (animation) { 1570 scrubTween ? scrubTween.progress(1) : !animation.paused() ? _endAnimation(animation, animation.reversed()) : isToggle || _endAnimation(animation, self.direction < 0, 1); 1571 } 1572 }; 1573 1574 self.labelToScroll = function (label) { 1575 return animation && animation.labels && (start || self.refresh() || start) + animation.labels[label] / animation.duration() * change || 0; 1576 }; 1577 1578 self.getTrailing = function (name) { 1579 var i = _triggers.indexOf(self), 1580 a = self.direction > 0 ? _triggers.slice(0, i).reverse() : _triggers.slice(i + 1); 1581 1582 return (_isString(name) ? a.filter(function (t) { 1583 return t.vars.preventOverlaps === name; 1584 }) : a).filter(function (t) { 1585 return self.direction > 0 ? t.end <= start : t.start >= end; 1586 }); 1587 }; 1588 1589 self.update = function (reset, recordVelocity, forceFake) { 1590 if (containerAnimation && !forceFake && !reset) { 1591 return; 1592 } 1593 1594 var scroll = _refreshingAll === true ? prevScroll : self.scroll(), 1595 p = reset ? 0 : (scroll - start) / change, 1596 clipped = p < 0 ? 0 : p > 1 ? 1 : p || 0, 1597 prevProgress = self.progress, 1598 isActive, 1599 wasActive, 1600 toggleState, 1601 action, 1602 stateChanged, 1603 toggled, 1604 isAtMax, 1605 isTakingAction; 1606 1607 if (recordVelocity) { 1608 scroll2 = scroll1; 1609 scroll1 = containerAnimation ? scrollFunc() : scroll; 1610 1611 if (snap) { 1612 snap2 = snap1; 1613 snap1 = animation && !isToggle ? animation.totalProgress() : clipped; 1614 } 1615 } // 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). 1616 1617 1618 anticipatePin && !clipped && pin && !_refreshing && !_startup && _lastScrollTime && start < scroll + (scroll - scroll2) / (_getTime() - _time2) * anticipatePin && (clipped = 0.0001); 1619 1620 if (clipped !== prevProgress && self.enabled) { 1621 isActive = self.isActive = !!clipped && clipped < 1; 1622 wasActive = !!prevProgress && prevProgress < 1; 1623 toggled = isActive !== wasActive;
vendor: 5,594 bytes, lines 1624-1721
1624 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) 1625 1626 self.direction = clipped > prevProgress ? 1 : -1; 1627 self.progress = clipped; 1628 1629 if (stateChanged && !_refreshing) { 1630 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. 1631 1632 if (isToggle) { 1633 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) 1634 1635 isTakingAction = animation && (action === "complete" || action === "reset" || action in animation); 1636 } 1637 } 1638 1639 preventOverlaps && (toggled || isTakingAction) && (isTakingAction || scrub || !animation) && (_isFunction(preventOverlaps) ? preventOverlaps(self) : self.getTrailing(preventOverlaps).forEach(function (t) { 1640 return t.endAnimation(); 1641 })); 1642 1643 if (!isToggle) { 1644 if (scrubTween && !_refreshing && !_startup) { 1645 scrubTween._dp._time - scrubTween._start !== scrubTween._time && 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. 1646 1647 if (scrubTween.resetTo) { 1648 scrubTween.resetTo("totalProgress", clipped, animation._tTime / animation._tDur); 1649 } else { 1650 // legacy support (courtesy), before 3.10.0 1651 scrubTween.vars.totalProgress = clipped; 1652 scrubTween.invalidate().restart(); 1653 } 1654 } else if (animation) { 1655 animation.totalProgress(clipped, !!(_refreshing && (lastRefresh || reset))); 1656 } 1657 } 1658 1659 if (pin) { 1660 reset && pinSpacing && (spacer.style[pinSpacing + direction.os2] = spacingStart); 1661 1662 if (!useFixedPosition) { 1663 pinSetter(_round(pinStart + pinChange * clipped)); 1664 } else if (stateChanged) { 1665 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) 1666 1667 if (pinReparent) { 1668 if (!reset && (isActive || isAtMax)) { 1669 var bounds = _getBounds(pin, true), 1670 _offset = scroll - start; 1671 1672 _reparent(pin, _body, bounds.top + (direction === _vertical ? _offset : 0) + _px, bounds.left + (direction === _vertical ? 0 : _offset) + _px); 1673 } else { 1674 _reparent(pin, spacer); 1675 } 1676 } 1677 1678 _setState(isActive || isAtMax ? pinActiveState : pinState); 1679 1680 pinMoves && clipped < 1 && isActive || pinSetter(pinStart + (clipped === 1 && !isAtMax ? pinChange : 0)); 1681 } 1682 } 1683 1684 snap && !tweenTo.tween && !_refreshing && !_startup && snapDelayedCall.restart(true); 1685 toggleClass && (toggled || once && clipped && (clipped < 1 || !_limitCallbacks)) && _toArray(toggleClass.targets).forEach(function (el) { 1686 return el.classList[isActive || once ? "add" : "remove"](toggleClass.className); 1687 }); // classes could affect positioning, so do it even if reset or refreshing is true. 1688 1689 onUpdate && !isToggle && !reset && onUpdate(self); 1690 1691 if (stateChanged && !_refreshing) { 1692 if (isToggle) { 1693 if (isTakingAction) { 1694 if (action === "complete") { 1695 animation.pause().totalProgress(1); 1696 } else if (action === "reset") { 1697 animation.restart(true).pause(); 1698 } else if (action === "restart") { 1699 animation.restart(true); 1700 } else { 1701 animation[action](); 1702 } 1703 } 1704 1705 onUpdate && onUpdate(self); 1706 } 1707 1708 if (toggled || !_limitCallbacks) { 1709 // 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. 1710 onToggle && toggled && _callback(self, onToggle); 1711 callbacks[toggleState] && _callback(self, callbacks[toggleState]); 1712 once && (clipped === 1 ? self.kill(false, 1) : callbacks[toggleState] = 0); // a callback shouldn't be called again if once is true. 1713 1714 if (!toggled) { 1715 // 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 1716 toggleState = clipped === 1 ? 1 : 3; 1717 callbacks[toggleState] && _callback(self, callbacks[toggleState]); 1718 } 1719 } 1720 1721 if (fastScrollEnd && !isActive && Math.abs(self.getVelocity()) > (_isNumber(fastScrollEnd) ? fastScrollEnd : 2500)) {
vendor: 4,133 bytes, lines 1722-1831
1722 _endAnimation(self.callbackAnimation); 1723 1724 scrubTween ? scrubTween.progress(1) : _endAnimation(animation, action === "reverse" ? 1 : !clipped, 1); 1725 } 1726 } else if (isToggle && onUpdate && !_refreshing) { 1727 onUpdate(self); 1728 } 1729 } // update absolutely-positioned markers (only if the scroller isn't the viewport) 1730 1731 1732 if (markerEndSetter) { 1733 var n = containerAnimation ? scroll / containerAnimation.duration() * (containerAnimation._caScrollDist || 0) : scroll; 1734 markerStartSetter(n + (markerStartTrigger._isFlipped ? 1 : 0)); 1735 markerEndSetter(n); 1736 } 1737 1738 caMarkerSetter && caMarkerSetter(-scroll / containerAnimation.duration() * (containerAnimation._caScrollDist || 0)); 1739 }; 1740 1741 self.enable = function (reset, refresh) { 1742 if (!self.enabled) { 1743 self.enabled = true; 1744 1745 _addListener(scroller, "resize", _onResize); 1746 1747 isViewport || _addListener(scroller, "scroll", _onScroll); 1748 onRefreshInit && _addListener(ScrollTrigger, "refreshInit", onRefreshInit); 1749 1750 if (reset !== false) { 1751 self.progress = prevProgress = 0; 1752 scroll1 = scroll2 = lastSnap = scrollFunc(); 1753 } 1754 1755 refresh !== false && self.refresh(); 1756 } 1757 }; 1758 1759 self.getTween = function (snap) { 1760 return snap && tweenTo ? tweenTo.tween : scrubTween; 1761 }; 1762 1763 self.setPositions = function (newStart, newEnd, keepClamp, pinOffset) { 1764 // 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() 1765 if (containerAnimation) { 1766 // convert ratios into scroll positions. Remember, start/end values on ScrollTriggers that have a containerAnimation refer to the time (in seconds), NOT scroll positions. 1767 var st = containerAnimation.scrollTrigger, 1768 duration = containerAnimation.duration(), 1769 _change = st.end - st.start; 1770 1771 newStart = st.start + _change * newStart / duration; 1772 newEnd = st.start + _change * newEnd / duration; 1773 } 1774 1775 self.refresh(false, false, { 1776 start: _keepClamp(newStart, keepClamp && !!self._startClamp), 1777 end: _keepClamp(newEnd, keepClamp && !!self._endClamp) 1778 }, pinOffset); 1779 self.update(); 1780 }; 1781 1782 self.adjustPinSpacing = function (amount) { 1783 if (spacerState && amount) { 1784 var i = spacerState.indexOf(direction.d) + 1; 1785 spacerState[i] = parseFloat(spacerState[i]) + amount + _px; 1786 spacerState[1] = parseFloat(spacerState[1]) + amount + _px; 1787 1788 _setState(spacerState); 1789 } 1790 }; 1791 1792 self.disable = function (reset, allowAnimation) { 1793 if (self.enabled) { 1794 reset !== false && self.revert(true, true); 1795 self.enabled = self.isActive = false; 1796 allowAnimation || scrubTween && scrubTween.pause(); 1797 prevScroll = 0; 1798 pinCache && (pinCache.uncache = 1); 1799 onRefreshInit && _removeListener(ScrollTrigger, "refreshInit", onRefreshInit); 1800 1801 if (snapDelayedCall) { 1802 snapDelayedCall.pause(); 1803 tweenTo.tween && tweenTo.tween.kill() && (tweenTo.tween = 0); 1804 } 1805 1806 if (!isViewport) { 1807 var i = _triggers.length; 1808 1809 while (i--) { 1810 if (_triggers[i].scroller === scroller && _triggers[i] !== self) { 1811 return; //don't remove the listeners if there are still other triggers referencing it. 1812 } 1813 } 1814 1815 _removeListener(scroller, "resize", _onResize); 1816 1817 isViewport || _removeListener(scroller, "scroll", _onScroll); 1818 } 1819 } 1820 }; 1821 1822 self.kill = function (revert, allowAnimation) { 1823 self.disable(revert, allowAnimation); 1824 scrubTween && !allowAnimation && scrubTween.kill(); 1825 id && delete _ids[id]; 1826 1827 var i = _triggers.indexOf(self); 1828 1829 i >= 0 && _triggers.splice(i, 1); 1830 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... 1831 // if no other ScrollTrigger instances of the same scroller are found, wipe out any re
vendor: 4,816 bytes, lines 1831-1961
1831corded scroll position. Otherwise, in a single page application, for example, it could maintain scroll position when it really shouldn't. 1832 1833 i = 0; 1834 1835 _triggers.forEach(function (t) { 1836 return t.scroller === self.scroller && (i = 1); 1837 }); 1838 1839 i || _refreshingAll || (self.scroll.rec = 0); 1840 1841 if (animation) { 1842 animation.scrollTrigger = null; 1843 revert && animation.revert({ 1844 kill: false 1845 }); 1846 allowAnimation || animation.kill(); 1847 } 1848 1849 markerStart && [markerStart, markerEnd, markerStartTrigger, markerEndTrigger].forEach(function (m) { 1850 return m.parentNode && m.parentNode.removeChild(m); 1851 }); 1852 _primary === self && (_primary = 0); 1853 1854 if (pin) { 1855 pinCache && (pinCache.uncache = 1); 1856 i = 0; 1857 1858 _triggers.forEach(function (t) { 1859 return t.pin === pin && i++; 1860 }); 1861 1862 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. 1863 } 1864 1865 vars.onKill && vars.onKill(self); 1866 }; 1867 1868 _triggers.push(self); 1869 1870 self.enable(false, false); 1871 customRevertReturn && customRevertReturn(self); 1872 1873 if (animation && animation.add && !change) { 1874 // 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. 1875 var updateFunc = self.update; // some browsers may fire a scroll event BEFORE a tick elapses and/or the DOMContentLoaded fires. So there's a chance update() will be called BEFORE a refresh() has happened on a Timeline-attached ScrollTrigger which means the start/end won't be calculated yet. We don't want to add conditional logic inside the update() method (like check to see if end is defined and if not, force a refresh()) because that's a function that gets hit a LOT (performance). So we swap out the real update() method for this one that'll re-attach it the first time it gets called and of course forces a refresh(). 1876 1877 self.update = function () { 1878 self.update = updateFunc; 1879 start || end || self.refresh(); 1880 }; 1881 1882 gsap.delayedCall(0.01, self.update); 1883 change = 0.01; 1884 start = end = 0; 1885 } else { 1886 self.refresh(); 1887 } 1888 1889 pin && _queueRefreshAll(); // pinning could affect the positions of other things, so make sure we queue a full refresh() 1890 }; 1891 1892 ScrollTrigger.register = function register(core) { 1893 if (!_coreInitted) { 1894 gsap = core || _getGSAP(); 1895 _windowExists() && window.document && ScrollTrigger.enable(); 1896 _coreInitted = _enabled; 1897 } 1898 1899 return _coreInitted; 1900 }; 1901 1902 ScrollTrigger.defaults = function defaults(config) { 1903 if (config) { 1904 for (var p in config) { 1905 _defaults[p] = config[p]; 1906 } 1907 } 1908 1909 return _defaults; 1910 }; 1911 1912 ScrollTrigger.disable = function disable(reset, kill) { 1913 _enabled = 0; 1914 1915 _triggers.forEach(function (trigger) { 1916 return trigger[kill ? "kill" : "disable"](reset); 1917 }); 1918 1919 _removeListener(_win, "wheel", _onScroll); 1920 1921 _removeListener(_doc, "scroll", _onScroll); 1922 1923 clearInterval(_syncInterval); 1924 1925 _removeListener(_doc, "touchcancel", _passThrough); 1926 1927 _removeListener(_body, "touchstart", _passThrough); 1928 1929 _multiListener(_removeListener, _doc, "pointerdown,touchstart,mousedown", _pointerDownHandler); 1930 1931 _multiListener(_removeListener, _doc, "pointerup,touchend,mouseup", _pointerUpHandler); 1932 1933 _resizeDelay.kill(); 1934 1935 _iterateAutoRefresh(_removeListener); 1936 1937 for (var i = 0; i < _scrollers.length; i += 3) { 1938 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 1]); 1939 1940 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 2]); 1941 } 1942 }; 1943 1944 ScrollTrigger.enable = function enable() { 1945 _win = window; 1946 _doc = document; 1947 _docEl = _doc.documentElement; 1948 _body = _doc.body; 1949 1950 if (gsap) { 1951 _toArray = gsap.utils.toArray; 1952 _clamp = gsap.utils.clamp; 1953 _context = gsap.core.context || _passThrough; 1954 _suppressOverwrites = gsap.core.suppressOverwrites || _passThrough; 1955 _scrollRestoration = _win.history.scrollRestoration || "auto"; 1956 _lastScroll = _win.pageYOffset; 1957 gsap.core.globals("ScrollTrigger", ScrollTrigger); // must register the global manually because in Internet Explorer, functions (classes) don't have a "name" property. 1958 1959 if (_body) { 1960 _enabled = 1; 1961 _div100vh = document.createElement("div"); // to solve mobile browser address bar show/hide resizing, we shouldn't rely on window.innerHeight. Instead, use a <div>
vendor: 4,791 bytes, lines 1961-2071
1961 with its height set to 100vh and measure that since that's what the scrolling is based on anyway and it's not affected by address bar showing/hiding. 1962 1963 _div100vh.style.height = "100vh"; 1964 _div100vh.style.position = "absolute"; 1965 1966 _refresh100vh(); 1967 1968 _rafBugFix(); 1969 1970 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. 1971 1972 ScrollTrigger.isTouch = Observer.isTouch; 1973 _fixIOSBug = Observer.isTouch && /(iPad|iPhone|iPod|Mac)/g.test(navigator.userAgent); // since 2017, iOS has had a bug that causes event.clientX/Y to be inaccurate when a scroll occurs, thus we must alternate ignoring every other touchmove event to work around it. See https://bugs.webkit.org/show_bug.cgi?id=181954 and https://codepen.io/GreenSock/pen/ExbrPNa/087cef197dc35445a0951e8935c41503 1974 1975 _addListener(_win, "wheel", _onScroll); // mostly for 3rd party smooth scrolling libraries. 1976 1977 1978 _root = [_win, _doc, _docEl, _body]; 1979 1980 if (gsap.matchMedia) { 1981 ScrollTrigger.matchMedia = function (vars) { 1982 var mm = gsap.matchMedia(), 1983 p; 1984 1985 for (p in vars) { 1986 mm.add(p, vars[p]); 1987 } 1988 1989 return mm; 1990 }; 1991 1992 gsap.addEventListener("matchMediaInit", function () { 1993 return _revertAll(); 1994 }); 1995 gsap.addEventListener("matchMediaRevert", function () { 1996 return _revertRecorded(); 1997 }); 1998 gsap.addEventListener("matchMedia", function () { 1999 _refreshAll(0, 1); 2000 2001 _dispatch("matchMedia"); 2002 }); 2003 gsap.matchMedia("(orientation: portrait)", function () { 2004 // when orientation changes, we should take new base measurements for the ignoreMobileResize feature. 2005 _setBaseDimensions(); 2006 2007 return _setBaseDimensions; 2008 }); 2009 } else { 2010 console.warn("Requires GSAP 3.11.0 or later"); 2011 } 2012 2013 _setBaseDimensions(); 2014 2015 _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! 2016 2017 2018 var bodyStyle = _body.style, 2019 border = bodyStyle.borderTopStyle, 2020 AnimationProto = gsap.core.Animation.prototype, 2021 bounds, 2022 i; 2023 AnimationProto.revert || Object.defineProperty(AnimationProto, "revert", { 2024 value: function value() { 2025 return this.time(-0.01, true); 2026 } 2027 }); // only for backwards compatibility (Animation.revert() was added after 3.10.4) 2028 2029 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. 2030 2031 bounds = _getBounds(_body); 2032 _vertical.m = Math.round(bounds.top + _vertical.sc()) || 0; // accommodate the offset of the <body> caused by margins and/or padding 2033 2034 _horizontal.m = Math.round(bounds.left + _horizontal.sc()) || 0; 2035 border ? bodyStyle.borderTopStyle = border : bodyStyle.removeProperty("border-top-style"); // TODO: (?) maybe move to leveraging the velocity mechanism in Observer and skip intervals. 2036 2037 _syncInterval = setInterval(_sync, 250); 2038 gsap.delayedCall(0.5, function () { 2039 return _startup = 0; 2040 }); 2041 2042 _addListener(_doc, "touchcancel", _passThrough); // some older Android devices intermittently stop dispatching "touchmove" events if we don't listen for "touchcancel" on the document. 2043 2044 2045 _addListener(_body, "touchstart", _passThrough); //works around Safari bug: https://greensock.com/forums/topic/21450-draggable-in-iframe-on-mobile-is-buggy/ 2046 2047 2048 _multiListener(_addListener, _doc, "pointerdown,touchstart,mousedown", _pointerDownHandler); 2049 2050 _multiListener(_addListener, _doc, "pointerup,touchend,mouseup", _pointerUpHandler); 2051 2052 _transformProp = gsap.utils.checkPrefix("transform"); 2053 2054 _stateProps.push(_transformProp); 2055 2056 _coreInitted = _getTime(); 2057 _resizeDelay = gsap.delayedCall(0.2, _refreshAll).pause(); 2058 _autoRefresh = [_doc, "visibilitychange", function () { 2059 var w = _win.innerWidth, 2060 h = _win.innerHeight; 2061 2062 if (_doc.hidden) { 2063 _prevWidth = w; 2064 _prevHeight = h; 2065 } else if (_prevWidth !== w || _prevHeight !== h) { 2066 _onResize(); 2067 } 2068 }, _doc, "DOMContentLoaded", _refreshAll, _win, "load", _refreshAll, _win, "resize", _onResize]; 2069 2070 _iterateAutoRefresh(_addListener); 2071
vendor: 3,769 bytes, lines 2072-2171
2072 _triggers.forEach(function (trigger) { 2073 return trigger.enable(0, 1); 2074 }); 2075 2076 for (i = 0; i < _scrollers.length; i += 3) { 2077 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 1]); 2078 2079 _wheelListener(_removeListener, _scrollers[i], _scrollers[i + 2]); 2080 } 2081 } 2082 } 2083 }; 2084 2085 ScrollTrigger.config = function config(vars) { 2086 "limitCallbacks" in vars && (_limitCallbacks = !!vars.limitCallbacks); 2087 var ms = vars.syncInterval; 2088 ms && clearInterval(_syncInterval) || (_syncInterval = ms) && setInterval(_sync, ms); 2089 "ignoreMobileResize" in vars && (_ignoreMobileResize = ScrollTrigger.isTouch === 1 && vars.ignoreMobileResize); 2090 2091 if ("autoRefreshEvents" in vars) { 2092 _iterateAutoRefresh(_removeListener) || _iterateAutoRefresh(_addListener, vars.autoRefreshEvents || "none"); 2093 _ignoreResize = (vars.autoRefreshEvents + "").indexOf("resize") === -1; 2094 } 2095 }; 2096 2097 ScrollTrigger.scrollerProxy = function scrollerProxy(target, vars) { 2098 var t = _getTarget(target), 2099 i = _scrollers.indexOf(t), 2100 isViewport = _isViewport(t); 2101 2102 if (~i) { 2103 _scrollers.splice(i, isViewport ? 6 : 2); 2104 } 2105 2106 if (vars) { 2107 isViewport ? _proxies.unshift(_win, vars, _body, vars, _docEl, vars) : _proxies.unshift(t, vars); 2108 } 2109 }; 2110 2111 ScrollTrigger.clearMatchMedia = function clearMatchMedia(query) { 2112 _triggers.forEach(function (t) { 2113 return t._ctx && t._ctx.query === query && t._ctx.kill(true, true); 2114 }); 2115 }; 2116 2117 ScrollTrigger.isInViewport = function isInViewport(element, ratio, horizontal) { 2118 var bounds = (_isString(element) ? _getTarget(element) : element).getBoundingClientRect(), 2119 offset = bounds[horizontal ? _width : _height] * ratio || 0; 2120 return horizontal ? bounds.right - offset > 0 && bounds.left + offset < _win.innerWidth : bounds.bottom - offset > 0 && bounds.top + offset < _win.innerHeight; 2121 }; 2122 2123 ScrollTrigger.positionInViewport = function positionInViewport(element, referencePoint, horizontal) { 2124 _isString(element) && (element = _getTarget(element)); 2125 var bounds = element.getBoundingClientRect(), 2126 size = bounds[horizontal ? _width : _height], 2127 offset = referencePoint == null ? size / 2 : referencePoint in _keywords ? _keywords[referencePoint] * size : ~referencePoint.indexOf("%") ? parseFloat(referencePoint) * size / 100 : parseFloat(referencePoint) || 0; 2128 return horizontal ? (bounds.left + offset) / _win.innerWidth : (bounds.top + offset) / _win.innerHeight; 2129 }; 2130 2131 ScrollTrigger.killAll = function killAll(allowListeners) { 2132 _triggers.slice(0).forEach(function (t) { 2133 return t.vars.id !== "ScrollSmoother" && t.kill(); 2134 }); 2135 2136 if (allowListeners !== true) { 2137 var listeners = _listeners.killAll || []; 2138 _listeners = {}; 2139 listeners.forEach(function (f) { 2140 return f(); 2141 }); 2142 } 2143 }; 2144 2145 return ScrollTrigger; 2146}(); 2147ScrollTrigger.version = "3.12.2"; 2148 2149ScrollTrigger.saveStyles = function (targets) { 2150 return targets ? _toArray(targets).forEach(function (target) { 2151 // saved styles are recorded in a consecutive alternating Array, like [element, cssText, transform attribute, cache, matchMedia, ...] 2152 if (target && target.style) { 2153 var i = _savedStyles.indexOf(target); 2154 2155 i >= 0 && _savedStyles.splice(i, 5); 2156 2157 _savedStyles.push(target, target.style.cssText, target.getBBox && target.getAttribute("transform"), gsap.core.getCache(target), _context()); 2158 } 2159 }) : _savedStyles; 2160}; 2161 2162ScrollTrigger.revert = function (soft, media) { 2163 return _revertAll(!soft, media); 2164}; 2165 2166ScrollTrigger.create = function (vars, animation) { 2167 return new ScrollTrigger(vars, animation); 2168}; 2169 2170ScrollTrigger.refresh = function (safe) { 2171 return safe ? _onResize(
vendor: 13,931 bytes, lines 2171-2544
2171) : (_coreInitted || ScrollTrigger.register()) && _refreshAll(true); 2172}; 2173 2174ScrollTrigger.update = function (force) { 2175 return ++_scrollers.cache && _updateAll(force === true ? 2 : 0); 2176}; 2177 2178ScrollTrigger.clearScrollMemory = _clearScrollMemory; 2179 2180ScrollTrigger.maxScroll = function (element, horizontal) { 2181 return _maxScroll(element, horizontal ? _horizontal : _vertical); 2182}; 2183 2184ScrollTrigger.getScrollFunc = function (element, horizontal) { 2185 return _getScrollFunc(_getTarget(element), horizontal ? _horizontal : _vertical); 2186}; 2187 2188ScrollTrigger.getById = function (id) { 2189 return _ids[id]; 2190}; 2191 2192ScrollTrigger.getAll = function () { 2193 return _triggers.filter(function (t) { 2194 return t.vars.id !== "ScrollSmoother"; 2195 }); 2196}; // 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. 2197 2198 2199ScrollTrigger.isScrolling = function () { 2200 return !!_lastScrollTime; 2201}; 2202 2203ScrollTrigger.snapDirectional = _snapDirectional; 2204 2205ScrollTrigger.addEventListener = function (type, callback) { 2206 var a = _listeners[type] || (_listeners[type] = []); 2207 ~a.indexOf(callback) || a.push(callback); 2208}; 2209 2210ScrollTrigger.removeEventListener = function (type, callback) { 2211 var a = _listeners[type], 2212 i = a && a.indexOf(callback); 2213 i >= 0 && a.splice(i, 1); 2214}; 2215 2216ScrollTrigger.batch = function (targets, vars) { 2217 var result = [], 2218 varsCopy = {}, 2219 interval = vars.interval || 0.016, 2220 batchMax = vars.batchMax || 1e9, 2221 proxyCallback = function proxyCallback(type, callback) { 2222 var elements = [], 2223 triggers = [], 2224 delay = gsap.delayedCall(interval, function () { 2225 callback(elements, triggers); 2226 elements = []; 2227 triggers = []; 2228 }).pause(); 2229 return function (self) { 2230 elements.length || delay.restart(true); 2231 elements.push(self.trigger); 2232 triggers.push(self); 2233 batchMax <= elements.length && delay.progress(1); 2234 }; 2235 }, 2236 p; 2237 2238 for (p in vars) { 2239 varsCopy[p] = p.substr(0, 2) === "on" && _isFunction(vars[p]) && p !== "onRefreshInit" ? proxyCallback(p, vars[p]) : vars[p]; 2240 } 2241 2242 if (_isFunction(batchMax)) { 2243 batchMax = batchMax(); 2244 2245 _addListener(ScrollTrigger, "refresh", function () { 2246 return batchMax = vars.batchMax(); 2247 }); 2248 } 2249 2250 _toArray(targets).forEach(function (target) { 2251 var config = {}; 2252 2253 for (p in varsCopy) { 2254 config[p] = varsCopy[p]; 2255 } 2256 2257 config.trigger = target; 2258 result.push(ScrollTrigger.create(config)); 2259 }); 2260 2261 return result; 2262}; // 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). 2263 2264 2265var _clampScrollAndGetDurationMultiplier = function _clampScrollAndGetDurationMultiplier(scrollFunc, current, end, max) { 2266 current > max ? scrollFunc(max) : current < 0 && scrollFunc(0); 2267 return end > max ? (max - current) / (end - current) : end < 0 ? current / (current - end) : 1; 2268}, 2269 _allowNativePanning = function _allowNativePanning(target, direction) { 2270 if (direction === true) { 2271 target.style.removeProperty("touch-action"); 2272 } else { 2273 target.style.touchAction = direction === true ? "auto" : direction ? "pan-" + direction + (Observer.isTouch ? " pinch-zoom" : "") : "none"; // note: Firefox doesn't support it pinch-zoom properly, at least in addition to a pan-x or pan-y. 2274 } 2275 2276 target === _docEl && _allowNativePanning(_body, direction); 2277}, 2278 _overflow = { 2279 auto: 1, 2280 scroll: 1 2281}, 2282 _nestedScroll = function _nestedScroll(_ref5) { 2283 var event = _ref5.event, 2284 target = _ref5.target, 2285 axis = _ref5.axis; 2286 2287 var node = (event.changedTouches ? event.changedTouches[0] : event).target, 2288 cache = node._gsap || gsap.core.getCache(node), 2289 time = _getTime(), 2290 cs; 2291 2292 if (!cache._isScrollT || time - cache._isScrollT > 2000) { 2293 // cache for 2 seconds to improve performance. 2294 while (node && node !== _body && (node.scrollHeight <= node.clientHeight && node.scrollWidth <= node.clientWidth || !(_overflow[(cs = _getComputedStyle(node)).overflowY] || _overflow[cs.overflowX]))) { 2295 node = node.parentNode; 2296 } 2297 2298 cache._isScroll = node && node !== target && !_isViewport(node) && (_overflow[(cs = _getComputedStyle(node)).overflowY] || _overflow[cs.overflowX]); 2299 cache._isScrollT = time; 2300 } 2301 2302 if (cache._isScroll || axis === "x") { 2303 event.stopPropagation(); 2304 event._gsapAllow = true; 2305 } 2306}, 2307 // capture events on scrollable elements INSIDE the <body> and allow those by calling stopPropagation() when we find a scrollable ancestor 2308_inputObserver = function _inputObserver(target, type, inputs, nested) { 2309 return Observer.create({ 2310 target: target, 2311 capture: true, 2312 debounce: false, 2313 lockAxis: true, 2314 type: type, 2315 onWheel: nested = nested && _nestedScroll, 2316 onPress: nested, 2317 onDrag: nested, 2318 onScroll: nested, 2319 onEnable: function onEnable() { 2320 return inputs && _addListener(_doc, Observer.eventTypes[0], _captureInputs, false, true); 2321 }, 2322 onDisable: function onDisable() { 2323 return _removeListener(_doc, Observer.eventTypes[0], _captureInputs, true); 2324 } 2325 }); 2326}, 2327 _inputExp = /(input|label|select|textarea)/i, 2328 _inputIsFocused, 2329 _captureInputs = function _captureInputs(e) { 2330 var isInput = _inputExp.test(e.target.tagName); 2331 2332 if (isInput || _inputIsFocused) { 2333 e._gsapAllow = true; 2334 _inputIsFocused = isInput; 2335 } 2336}, 2337 _getScrollNormalizer = function _getScrollNormalizer(vars) { 2338 _isObject(vars) || (vars = {}); 2339 vars.preventDefault = vars.isNormalizer = vars.allowClicks = true; 2340 vars.type || (vars.type = "wheel,touch"); 2341 vars.debounce = !!vars.debounce; 2342 vars.id = vars.id || "normalizer"; 2343 2344 var _vars2 = vars, 2345 normalizeScrollX = _vars2.normalizeScrollX, 2346 momentum = _vars2.momentum, 2347 allowNestedScroll = _vars2.allowNestedScroll, 2348 onRelease = _vars2.onRelease, 2349 self, 2350 maxY, 2351 target = _getTarget(vars.target) || _docEl, 2352 smoother = gsap.core.globals().ScrollSmoother, 2353 smootherInstance = smoother && smoother.get(), 2354 content = _fixIOSBug && (vars.content && _getTarget(vars.content) || smootherInstance && vars.content !== false && !smootherInstance.smooth() && smootherInstance.content()), 2355 scrollFuncY = _getScrollFunc(target, _vertical), 2356 scrollFuncX = _getScrollFunc(target, _horizontal), 2357 scale = 1, 2358 initialScale = (Observer.isTouch && _win.visualViewport ? _win.visualViewport.scale * _win.visualViewport.width : _win.outerWidth) / _win.innerWidth, 2359 wheelRefresh = 0, 2360 resolveMomentumDuration = _isFunction(momentum) ? function () { 2361 return momentum(self); 2362 } : function () { 2363 return momentum || 2.8; 2364 }, 2365 lastRefreshID, 2366 skipTouchMove, 2367 inputObserver = _inputObserver(target, vars.type, true, allowNestedScroll), 2368 resumeTouchMove = function resumeTouchMove() { 2369 return skipTouchMove = false; 2370 }, 2371 scrollClampX = _passThrough, 2372 scrollClampY = _passThrough, 2373 updateClamps = function updateClamps() { 2374 maxY = _maxScroll(target, _vertical); 2375 scrollClampY = _clamp(_fixIOSBug ? 1 : 0, maxY); 2376 normalizeScrollX && (scrollClampX = _clamp(0, _maxScroll(target, _horizontal))); 2377 lastRefreshID = _refreshID; 2378 }, 2379 removeContentOffset = function removeContentOffset() { 2380 content._gsap.y = _round(parseFloat(content._gsap.y) + scrollFuncY.offset) + "px"; 2381 content.style.transform = "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, " + parseFloat(content._gsap.y) + ", 0, 1)"; 2382 scrollFuncY.offset = scrollFuncY.cacheID = 0; 2383 }, 2384 ignoreDrag = function ignoreDrag() { 2385 if (skipTouchMove) { 2386 requestAnimationFrame(resumeTouchMove); 2387 2388 var offset = _round(self.deltaY / 2), 2389 scroll = scrollClampY(scrollFuncY.v - offset); 2390 2391 if (content && scroll !== scrollFuncY.v + scrollFuncY.offset) { 2392 scrollFuncY.offset = scroll - scrollFuncY.v; 2393 2394 var y = _round((parseFloat(content && content._gsap.y) || 0) - scrollFuncY.offset); 2395 2396 content.style.transform = "matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, " + y + ", 0, 1)"; 2397 content._gsap.y = y + "px"; 2398 scrollFuncY.cacheID = _scrollers.cache; 2399 2400 _updateAll(); 2401 } 2402 2403 return true; 2404 } 2405 2406 scrollFuncY.offset && removeContentOffset(); 2407 skipTouchMove = true; 2408 }, 2409 tween, 2410 startScrollX, 2411 startScrollY, 2412 onStopDelayedCall, 2413 onResize = function onResize() { 2414 // 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. 2415 updateClamps(); 2416 2417 if (tween.isActive() && tween.vars.scrollY > maxY) { 2418 scrollFuncY() > maxY ? tween.progress(1) && scrollFuncY(maxY) : tween.resetTo("scrollY", maxY); 2419 } 2420 }; 2421 2422 content && gsap.set(content, { 2423 y: "+=0" 2424 }); // to ensure there's a cache (element._gsap) 2425 2426 vars.ignoreCheck = function (e) { 2427 return _fixIOSBug && e.type === "touchmove" && ignoreDrag(e) || scale > 1.05 && e.type !== "touchstart" || self.isGesturing || e.touches && e.touches.length > 1; 2428 }; 2429 2430 vars.onPress = function () { 2431 skipTouchMove = false; 2432 var prevScale = scale; 2433 scale = _round((_win.visualViewport && _win.visualViewport.scale || 1) / initialScale); 2434 tween.pause(); 2435 prevScale !== scale && _allowNativePanning(target, scale > 1.01 ? true : normalizeScrollX ? false : "x"); 2436 startScrollX = scrollFuncX(); 2437 startScrollY = scrollFuncY(); 2438 updateClamps(); 2439 lastRefreshID = _refreshID; 2440 }; 2441 2442 vars.onRelease = vars.onGestureStart = function (self, wasDragging) { 2443 scrollFuncY.offset && removeContentOffset(); 2444 2445 if (!wasDragging) { 2446 onStopDelayedCall.restart(true); 2447 } else { 2448 _scrollers.cache++; // make sure we're pulling the non-cached value 2449 // 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) 2450 2451 var dur = resolveMomentumDuration(), 2452 currentScroll, 2453 endScroll; 2454 2455 if (normalizeScrollX) { 2456 currentScroll = scrollFuncX(); 2457 endScroll = currentScroll + dur * 0.05 * -self.velocityX / 0.227; // the constant .227 is from power4(0.05). velocity is inverted because scrolling goes in the opposite direction. 2458 2459 dur *= _clampScrollAndGetDurationMultiplier(scrollFuncX, currentScroll, endScroll, _maxScroll(target, _horizontal)); 2460 tween.vars.scrollX = scrollClampX(endScroll); 2461 } 2462 2463 currentScroll = scrollFuncY(); 2464 endScroll = currentScroll + dur * 0.05 * -self.velocityY / 0.227; // the constant .227 is from power4(0.05) 2465 2466 dur *= _clampScrollAndGetDurationMultiplier(scrollFuncY, currentScroll, endScroll, _maxScroll(target, _vertical)); 2467 tween.vars.scrollY = scrollClampY(endScroll); 2468 tween.invalidate().duration(dur).play(0.01); 2469 2470 if (_fixIOSBug && tween.vars.scrollY >= maxY || currentScroll >= maxY - 1) { 2471 // iOS bug: it'll show the address bar but NOT fire the window "resize" event until the animation is done but we must protect against overshoot so we leverage an onUpdate to do so. 2472 gsap.to({}, { 2473 onUpdate: onResize, 2474 duration: dur 2475 }); 2476 } 2477 } 2478 2479 onRelease && onRelease(self); 2480 }; 2481 2482 vars.onWheel = function () { 2483 tween._ts && tween.pause(); 2484 2485 if (_getTime() - wheelRefresh > 1000) { 2486 // after 1 second, refresh the clamps otherwise that'll only happen when ScrollTrigger.refresh() is called or for touch-scrolling. 2487 lastRefreshID = 0; 2488 wheelRefresh = _getTime(); 2489 } 2490 }; 2491 2492 vars.onChange = function (self, dx, dy, xArray, yArray) { 2493 _refreshID !== lastRefreshID && updateClamps(); 2494 dx && normalizeScrollX && scrollFuncX(scrollClampX(xArray[2] === dx ? startScrollX + (self.startX - self.x) : scrollFuncX() + dx - xArray[1])); // for more precision, we track pointer/touch movement from the start, otherwise it'll drift. 2495 2496 if (dy) { 2497 scrollFuncY.offset && removeContentOffset(); 2498 var isTouch = yArray[2] === dy, 2499 y = isTouch ? startScrollY + self.startY - self.y : scrollFuncY() + dy - yArray[1], 2500 yClamped = scrollClampY(y); 2501 isTouch && y !== yClamped && (startScrollY += yClamped - y); 2502 scrollFuncY(yClamped); 2503 } 2504 2505 (dy || dx) && _updateAll(); 2506 }; 2507 2508 vars.onEnable = function () { 2509 _allowNativePanning(target, normalizeScrollX ? false : "x"); 2510 2511 ScrollTrigger.addEventListener("refresh", onResize); 2512 2513 _addListener(_win, "resize", onResize); 2514 2515 if (scrollFuncY.smooth) { 2516 scrollFuncY.target.style.scrollBehavior = "auto"; 2517 scrollFuncY.smooth = scrollFuncX.smooth = false; 2518 } 2519 2520 inputObserver.enable(); 2521 }; 2522 2523 vars.onDisable = function () { 2524 _allowNativePanning(target, true); 2525 2526 _removeListener(_win, "resize", onResize); 2527 2528 ScrollTrigger.removeEventListener("refresh", onResize); 2529 inputObserver.kill(); 2530 }; 2531 2532 vars.lockAxis = vars.lockAxis !== false; 2533 self = new Observer(vars); 2534 self.iOS = _fixIOSBug; // used in the Observer getCachedScroll() function to work around an iOS bug that wreaks havoc with TouchEvent.clientY if we allow scroll to go all the way back to 0. 2535 2536 _fixIOSBug && !scrollFuncY() && scrollFuncY(1); // iOS bug causes event.clientY values to freak out (wildly inaccurate) if the scroll position is exactly 0. 2537 2538 _fixIOSBug && gsap.ticker.add(_passThrough); // prevent the ticker from sleeping 2539 2540 onStopDelayedCall = self._dc; 2541 tween = gsap.to(self, { 2542 ease: "power4", 2543 paused: true, 2544
2544scrollX: normalizeScrollX ? "+=0.1" : "+=0", 2545 scrollY: "+=0.1", 2546 modifiers: { 2547 scrollY: _interruptionTracker(scrollFuncY, scrollFuncY(), function () { 2548 return tween.pause(); 2549 }) 2550 }, 2551 onUpdate: _updateAll, 2552 onComplete: onStopDelayedCall.vars.onComplete 2553 }); // we need the modifier to sense if the scroll position is altered outside of the momentum tween (like with a scrollTo tween) so we can pause() it to prevent conflicts. 2554 2555 return self; 2556}; 2557 2558ScrollTrigger.sort = function (func) { 2559 return _triggers.sort(func || function (a, b) { 2560 return (a.vars.refreshPriority || 0) * -1e6 + a.start - (b.start + (b.vars.refreshPriority || 0) * -1e6); 2561 }); 2562}; 2563 2564ScrollTrigger.observe = function (vars) { 2565 return new Observer(vars); 2566}; 2567 2568ScrollTrigger.normalizeScroll = function (vars) { 2569 if (typeof vars === "undefined") { 2570 return _normalizer; 2571 } 2572 2573 if (vars === true && _normalizer) { 2574 return _normalizer.enable(); 2575 } 2576 2577 if (vars === false) { 2578 return _normalizer && _normalizer.kill(); 2579 } 2580 2581 var normalizer = vars instanceof Observer ? vars : _getScrollNormalizer(vars); 2582 _normalizer && _normalizer.target === normalizer.target && _normalizer.kill(); 2583 _isViewport(normalizer.target) && (_normalizer = normalizer); 2584 return normalizer; 2585}; 2586 2587ScrollTrigger.core = { 2588 // smaller file size way to leverage in ScrollSmoother and Observer 2589 _getVelocityProp: _getVelocityProp, 2590 _inputObserver: _inputObserver, 2591 _scrollers: _scrollers, 2592 _proxies: _proxies, 2593 bridge: { 2594 // when normalizeScroll sets the scroll position (ss = setScroll) 2595 ss: function ss() { 2596 _lastScrollTime || _dispatch("scrollStart"); 2597 _lastScrollTime = _getTime(); 2598 }, 2599 // a way to get the _refreshing value in Observer 2600 ref: function ref() { 2601 return _refreshing; 2602 } 2603 } 2604}; 2605_getGSAP() && gsap.registerPlugin(ScrollTrigger); 2606export { ScrollTrigger as default };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.