UNPKG

gsap

Version:

GSAP is a framework-agnostic JavaScript animation library that turns developers into animation superheroes. Build high-performance animations that work in **every** major browser. Animate CSS, SVG, canvas, React, Vue, WebGL, colors, strings, motion paths,

708 lines (597 loc) 26.1 kB
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /*! * Observer 3.13.0 * https://gsap.com * * @license Copyright 2008-2025, GreenSock. All rights reserved. * Subject to the terms at https://gsap.com/standard-license * @author: Jack Doyle, jack@greensock.com */ /* eslint-disable */ var gsap, _coreInitted, _clamp, _win, _doc, _docEl, _body, _isTouch, _pointerType, ScrollTrigger, _root, _normalizer, _eventTypes, _context, _getGSAP = function _getGSAP() { return gsap || typeof window !== "undefined" && (gsap = window.gsap) && gsap.registerPlugin && gsap; }, _startup = 1, _observers = [], _scrollers = [], _proxies = [], _getTime = Date.now, _bridge = function _bridge(name, value) { return value; }, _integrate = function _integrate() { var core = ScrollTrigger.core, data = core.bridge || {}, scrollers = core._scrollers, proxies = core._proxies; scrollers.push.apply(scrollers, _scrollers); proxies.push.apply(proxies, _proxies); _scrollers = scrollers; _proxies = proxies; _bridge = function _bridge(name, value) { return data[name](value); }; }, _getProxyProp = function _getProxyProp(element, property) { return ~_proxies.indexOf(element) && _proxies[_proxies.indexOf(element) + 1][property]; }, _isViewport = function _isViewport(el) { return !!~_root.indexOf(el); }, _addListener = function _addListener(element, type, func, passive, capture) { return element.addEventListener(type, func, { passive: passive !== false, capture: !!capture }); }, _removeListener = function _removeListener(element, type, func, capture) { return element.removeEventListener(type, func, !!capture); }, _scrollLeft = "scrollLeft", _scrollTop = "scrollTop", _onScroll = function _onScroll() { return _normalizer && _normalizer.isPressed || _scrollers.cache++; }, _scrollCacheFunc = function _scrollCacheFunc(f, doNotCache) { var cachingFunc = function cachingFunc(value) { // since reading the scrollTop/scrollLeft/pageOffsetY/pageOffsetX can trigger a layout, this function allows us to cache the value so it only gets read fresh after a "scroll" event fires (or while we're refreshing because that can lengthen the page and alter the scroll position). when "soft" is true, that means don't actually set the scroll, but cache the new value instead (useful in ScrollSmoother) if (value || value === 0) { _startup && (_win.history.scrollRestoration = "manual"); // otherwise the new position will get overwritten by the browser onload. var isNormalizing = _normalizer && _normalizer.isPressed; value = cachingFunc.v = Math.round(value) || (_normalizer && _normalizer.iOS ? 1 : 0); //TODO: iOS Bug: if you allow it to go to 0, Safari can start to report super strange (wildly inaccurate) touch positions! f(value); cachingFunc.cacheID = _scrollers.cache; isNormalizing && _bridge("ss", value); // set scroll (notify ScrollTrigger so it can dispatch a "scrollStart" event if necessary } else if (doNotCache || _scrollers.cache !== cachingFunc.cacheID || _bridge("ref")) { cachingFunc.cacheID = _scrollers.cache; cachingFunc.v = f(); } return cachingFunc.v + cachingFunc.offset; }; cachingFunc.offset = 0; return f && cachingFunc; }, _horizontal = { s: _scrollLeft, p: "left", p2: "Left", os: "right", os2: "Right", d: "width", d2: "Width", a: "x", sc: _scrollCacheFunc(function (value) { return arguments.length ? _win.scrollTo(value, _vertical.sc()) : _win.pageXOffset || _doc[_scrollLeft] || _docEl[_scrollLeft] || _body[_scrollLeft] || 0; }) }, _vertical = { s: _scrollTop, p: "top", p2: "Top", os: "bottom", os2: "Bottom", d: "height", d2: "Height", a: "y", op: _horizontal, sc: _scrollCacheFunc(function (value) { return arguments.length ? _win.scrollTo(_horizontal.sc(), value) : _win.pageYOffset || _doc[_scrollTop] || _docEl[_scrollTop] || _body[_scrollTop] || 0; }) }, _getTarget = function _getTarget(t, self) { return (self && self._ctx && self._ctx.selector || gsap.utils.toArray)(t)[0] || (typeof t === "string" && gsap.config().nullTargetWarn !== false ? console.warn("Element not found:", t) : null); }, _isWithin = function _isWithin(element, list) { // check if the element is in the list or is a descendant of an element in the list. var i = list.length; while (i--) { if (list[i] === element || list[i].contains(element)) { return true; } } return false; }, _getScrollFunc = function _getScrollFunc(element, _ref) { var s = _ref.s, sc = _ref.sc; // we store the scroller functions in an alternating sequenced Array like [element, verticalScrollFunc, horizontalScrollFunc, ...] so that we can minimize memory, maximize performance, and we also record the last position as a ".rec" property in order to revert to that after refreshing to ensure things don't shift around. _isViewport(element) && (element = _doc.scrollingElement || _docEl); var i = _scrollers.indexOf(element), offset = sc === _vertical.sc ? 1 : 2; !~i && (i = _scrollers.push(element) - 1); _scrollers[i + offset] || _addListener(element, "scroll", _onScroll); // clear the cache when a scroll occurs var prev = _scrollers[i + offset], func = prev || (_scrollers[i + offset] = _scrollCacheFunc(_getProxyProp(element, s), true) || (_isViewport(element) ? sc : _scrollCacheFunc(function (value) { return arguments.length ? element[s] = value : element[s]; }))); func.target = element; prev || (func.smooth = gsap.getProperty(element, "scrollBehavior") === "smooth"); // only set it the first time (don't reset every time a scrollFunc is requested because perhaps it happens during a refresh() when it's disabled in ScrollTrigger. return func; }, _getVelocityProp = function _getVelocityProp(value, minTimeRefresh, useDelta) { var v1 = value, v2 = value, t1 = _getTime(), t2 = t1, min = minTimeRefresh || 50, dropToZeroTime = Math.max(500, min * 3), update = function update(value, force) { var t = _getTime(); if (force || t - t1 > min) { v2 = v1; v1 = value; t2 = t1; t1 = t; } else if (useDelta) { v1 += value; } else { // not totally necessary, but makes it a bit more accurate by adjusting the v1 value according to the new slope. This way we're not just ignoring the incoming data. Removing for now because it doesn't seem to make much practical difference and it's probably not worth the kb. v1 = v2 + (value - v2) / (t - t2) * (t1 - t2); } }, reset = function reset() { v2 = v1 = useDelta ? 0 : v1; t2 = t1 = 0; }, getVelocity = function getVelocity(latestValue) { var tOld = t2, vOld = v2, t = _getTime(); (latestValue || latestValue === 0) && latestValue !== v1 && update(latestValue); return t1 === t2 || t - t2 > dropToZeroTime ? 0 : (v1 + (useDelta ? vOld : -vOld)) / ((useDelta ? t : t1) - tOld) * 1000; }; return { update: update, reset: reset, getVelocity: getVelocity }; }, _getEvent = function _getEvent(e, preventDefault) { preventDefault && !e._gsapAllow && e.preventDefault(); return e.changedTouches ? e.changedTouches[0] : e; }, _getAbsoluteMax = function _getAbsoluteMax(a) { var max = Math.max.apply(Math, a), min = Math.min.apply(Math, a); return Math.abs(max) >= Math.abs(min) ? max : min; }, _setScrollTrigger = function _setScrollTrigger() { ScrollTrigger = gsap.core.globals().ScrollTrigger; ScrollTrigger && ScrollTrigger.core && _integrate(); }, _initCore = function _initCore(core) { gsap = core || _getGSAP(); if (!_coreInitted && gsap && typeof document !== "undefined" && document.body) { _win = window; _doc = document; _docEl = _doc.documentElement; _body = _doc.body; _root = [_win, _doc, _docEl, _body]; _clamp = gsap.utils.clamp; _context = gsap.core.context || function () {}; _pointerType = "onpointerenter" in _body ? "pointer" : "mouse"; // isTouch is 0 if no touch, 1 if ONLY touch, and 2 if it can accommodate touch but also other types like mouse/pointer. _isTouch = Observer.isTouch = _win.matchMedia && _win.matchMedia("(hover: none), (pointer: coarse)").matches ? 1 : "ontouchstart" in _win || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 ? 2 : 0; _eventTypes = Observer.eventTypes = ("ontouchstart" in _docEl ? "touchstart,touchmove,touchcancel,touchend" : !("onpointerdown" in _docEl) ? "mousedown,mousemove,mouseup,mouseup" : "pointerdown,pointermove,pointercancel,pointerup").split(","); setTimeout(function () { return _startup = 0; }, 500); _setScrollTrigger(); _coreInitted = 1; } return _coreInitted; }; _horizontal.op = _vertical; _scrollers.cache = 0; export var Observer = /*#__PURE__*/function () { function Observer(vars) { this.init(vars); } var _proto = Observer.prototype; _proto.init = function init(vars) { _coreInitted || _initCore(gsap) || console.warn("Please gsap.registerPlugin(Observer)"); ScrollTrigger || _setScrollTrigger(); var tolerance = vars.tolerance, dragMinimum = vars.dragMinimum, type = vars.type, target = vars.target, lineHeight = vars.lineHeight, debounce = vars.debounce, preventDefault = vars.preventDefault, onStop = vars.onStop, onStopDelay = vars.onStopDelay, ignore = vars.ignore, wheelSpeed = vars.wheelSpeed, event = vars.event, onDragStart = vars.onDragStart, onDragEnd = vars.onDragEnd, onDrag = vars.onDrag, onPress = vars.onPress, onRelease = vars.onRelease, onRight = vars.onRight, onLeft = vars.onLeft, onUp = vars.onUp, onDown = vars.onDown, onChangeX = vars.onChangeX, onChangeY = vars.onChangeY, onChange = vars.onChange, onToggleX = vars.onToggleX, onToggleY = vars.onToggleY, onHover = vars.onHover, onHoverEnd = vars.onHoverEnd, onMove = vars.onMove, ignoreCheck = vars.ignoreCheck, isNormalizer = vars.isNormalizer, onGestureStart = vars.onGestureStart, onGestureEnd = vars.onGestureEnd, onWheel = vars.onWheel, onEnable = vars.onEnable, onDisable = vars.onDisable, onClick = vars.onClick, scrollSpeed = vars.scrollSpeed, capture = vars.capture, allowClicks = vars.allowClicks, lockAxis = vars.lockAxis, onLockAxis = vars.onLockAxis; this.target = target = _getTarget(target) || _docEl; this.vars = vars; ignore && (ignore = gsap.utils.toArray(ignore)); tolerance = tolerance || 1e-9; dragMinimum = dragMinimum || 0; wheelSpeed = wheelSpeed || 1; scrollSpeed = scrollSpeed || 1; type = type || "wheel,touch,pointer"; debounce = debounce !== false; lineHeight || (lineHeight = parseFloat(_win.getComputedStyle(_body).lineHeight) || 22); // note: browser may report "normal", so default to 22. var id, onStopDelayedCall, dragged, moved, wheeled, locked, axis, self = this, prevDeltaX = 0, prevDeltaY = 0, passive = vars.passive || !preventDefault && vars.passive !== false, scrollFuncX = _getScrollFunc(target, _horizontal), scrollFuncY = _getScrollFunc(target, _vertical), scrollX = scrollFuncX(), scrollY = scrollFuncY(), limitToTouch = ~type.indexOf("touch") && !~type.indexOf("pointer") && _eventTypes[0] === "pointerdown", // for devices that accommodate mouse events and touch events, we need to distinguish. isViewport = _isViewport(target), ownerDoc = target.ownerDocument || _doc, deltaX = [0, 0, 0], // wheel, scroll, pointer/touch deltaY = [0, 0, 0], onClickTime = 0, clickCapture = function clickCapture() { return onClickTime = _getTime(); }, _ignoreCheck = function _ignoreCheck(e, isPointerOrTouch) { return (self.event = e) && ignore && _isWithin(e.target, ignore) || isPointerOrTouch && limitToTouch && e.pointerType !== "touch" || ignoreCheck && ignoreCheck(e, isPointerOrTouch); }, onStopFunc = function onStopFunc() { self._vx.reset(); self._vy.reset(); onStopDelayedCall.pause(); onStop && onStop(self); }, update = function update() { var dx = self.deltaX = _getAbsoluteMax(deltaX), dy = self.deltaY = _getAbsoluteMax(deltaY), changedX = Math.abs(dx) >= tolerance, changedY = Math.abs(dy) >= tolerance; onChange && (changedX || changedY) && onChange(self, dx, dy, deltaX, deltaY); // in ScrollTrigger.normalizeScroll(), we need to know if it was touch/pointer so we need access to the deltaX/deltaY Arrays before we clear them out. if (changedX) { onRight && self.deltaX > 0 && onRight(self); onLeft && self.deltaX < 0 && onLeft(self); onChangeX && onChangeX(self); onToggleX && self.deltaX < 0 !== prevDeltaX < 0 && onToggleX(self); prevDeltaX = self.deltaX; deltaX[0] = deltaX[1] = deltaX[2] = 0; } if (changedY) { onDown && self.deltaY > 0 && onDown(self); onUp && self.deltaY < 0 && onUp(self); onChangeY && onChangeY(self); onToggleY && self.deltaY < 0 !== prevDeltaY < 0 && onToggleY(self); prevDeltaY = self.deltaY; deltaY[0] = deltaY[1] = deltaY[2] = 0; } if (moved || dragged) { onMove && onMove(self); if (dragged) { onDragStart && dragged === 1 && onDragStart(self); onDrag && onDrag(self); dragged = 0; } moved = false; } locked && !(locked = false) && onLockAxis && onLockAxis(self); if (wheeled) { onWheel(self); wheeled = false; } id = 0; }, onDelta = function onDelta(x, y, index) { deltaX[index] += x; deltaY[index] += y; self._vx.update(x); self._vy.update(y); debounce ? id || (id = requestAnimationFrame(update)) : update(); }, onTouchOrPointerDelta = function onTouchOrPointerDelta(x, y) { if (lockAxis && !axis) { self.axis = axis = Math.abs(x) > Math.abs(y) ? "x" : "y"; locked = true; } if (axis !== "y") { deltaX[2] += x; self._vx.update(x, true); // update the velocity as frequently as possible instead of in the debounced function so that very quick touch-scrolls (flicks) feel natural. If it's the mouse/touch/pointer, force it so that we get snappy/accurate momentum scroll. } if (axis !== "x") { deltaY[2] += y; self._vy.update(y, true); } debounce ? id || (id = requestAnimationFrame(update)) : update(); }, _onDrag = function _onDrag(e) { if (_ignoreCheck(e, 1)) { return; } e = _getEvent(e, preventDefault); var x = e.clientX, y = e.clientY, dx = x - self.x, dy = y - self.y, isDragging = self.isDragging; self.x = x; self.y = y; if (isDragging || (dx || dy) && (Math.abs(self.startX - x) >= dragMinimum || Math.abs(self.startY - y) >= dragMinimum)) { dragged = isDragging ? 2 : 1; // dragged: 0 = not dragging, 1 = first drag, 2 = normal drag isDragging || (self.isDragging = true); onTouchOrPointerDelta(dx, dy); } }, _onPress = self.onPress = function (e) { if (_ignoreCheck(e, 1) || e && e.button) { return; } self.axis = axis = null; onStopDelayedCall.pause(); self.isPressed = true; e = _getEvent(e); // note: may need to preventDefault(?) Won't side-scroll on iOS Safari if we do, though. prevDeltaX = prevDeltaY = 0; self.startX = self.x = e.clientX; self.startY = self.y = e.clientY; self._vx.reset(); // otherwise the t2 may be stale if the user touches and flicks super fast and releases in less than 2 requestAnimationFrame ticks, causing velocity to be 0. self._vy.reset(); _addListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, passive, true); self.deltaX = self.deltaY = 0; onPress && onPress(self); }, _onRelease = self.onRelease = function (e) { if (_ignoreCheck(e, 1)) { return; } _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true); var isTrackingDrag = !isNaN(self.y - self.startY), wasDragging = self.isDragging, isDragNotClick = wasDragging && (Math.abs(self.x - self.startX) > 3 || Math.abs(self.y - self.startY) > 3), // some touch devices need some wiggle room in terms of sensing clicks - the finger may move a few pixels. eventData = _getEvent(e); if (!isDragNotClick && isTrackingDrag) { self._vx.reset(); self._vy.reset(); //if (preventDefault && allowClicks && self.isPressed) { // check isPressed because in a rare edge case, the inputObserver in ScrollTrigger may stopPropagation() on the press/drag, so the onRelease may get fired without the onPress/onDrag ever getting called, thus it could trigger a click to occur on a link after scroll-dragging it. if (preventDefault && allowClicks) { gsap.delayedCall(0.08, function () { // some browsers (like Firefox) won't trust script-generated clicks, so if the user tries to click on a video to play it, for example, it simply won't work. Since a regular "click" event will most likely be generated anyway (one that has its isTrusted flag set to true), we must slightly delay our script-generated click so that the "real"/trusted one is prioritized. Remember, when there are duplicate events in quick succession, we suppress all but the first one. Some browsers don't even trigger the "real" one at all, so our synthetic one is a safety valve that ensures that no matter what, a click event does get dispatched. if (_getTime() - onClickTime > 300 && !e.defaultPrevented) { if (e.target.click) { //some browsers (like mobile Safari) don't properly trigger the click event e.target.click(); } else if (ownerDoc.createEvent) { var syntheticEvent = ownerDoc.createEvent("MouseEvents"); syntheticEvent.initMouseEvent("click", true, true, _win, 1, eventData.screenX, eventData.screenY, eventData.clientX, eventData.clientY, false, false, false, false, 0, null); e.target.dispatchEvent(syntheticEvent); } } }); } } self.isDragging = self.isGesturing = self.isPressed = false; onStop && wasDragging && !isNormalizer && onStopDelayedCall.restart(true); dragged && update(); // in case debouncing, we don't want onDrag to fire AFTER onDragEnd(). onDragEnd && wasDragging && onDragEnd(self); onRelease && onRelease(self, isDragNotClick); }, _onGestureStart = function _onGestureStart(e) { return e.touches && e.touches.length > 1 && (self.isGesturing = true) && onGestureStart(e, self.isDragging); }, _onGestureEnd = function _onGestureEnd() { return (self.isGesturing = false) || onGestureEnd(self); }, onScroll = function onScroll(e) { if (_ignoreCheck(e)) { return; } var x = scrollFuncX(), y = scrollFuncY(); onDelta((x - scrollX) * scrollSpeed, (y - scrollY) * scrollSpeed, 1); scrollX = x; scrollY = y; onStop && onStopDelayedCall.restart(true); }, _onWheel = function _onWheel(e) { if (_ignoreCheck(e)) { return; } e = _getEvent(e, preventDefault); onWheel && (wheeled = true); var multiplier = (e.deltaMode === 1 ? lineHeight : e.deltaMode === 2 ? _win.innerHeight : 1) * wheelSpeed; onDelta(e.deltaX * multiplier, e.deltaY * multiplier, 0); onStop && !isNormalizer && onStopDelayedCall.restart(true); }, _onMove = function _onMove(e) { if (_ignoreCheck(e)) { return; } var x = e.clientX, y = e.clientY, dx = x - self.x, dy = y - self.y; self.x = x; self.y = y; moved = true; onStop && onStopDelayedCall.restart(true); (dx || dy) && onTouchOrPointerDelta(dx, dy); }, _onHover = function _onHover(e) { self.event = e; onHover(self); }, _onHoverEnd = function _onHoverEnd(e) { self.event = e; onHoverEnd(self); }, _onClick = function _onClick(e) { return _ignoreCheck(e) || _getEvent(e, preventDefault) && onClick(self); }; onStopDelayedCall = self._dc = gsap.delayedCall(onStopDelay || 0.25, onStopFunc).pause(); self.deltaX = self.deltaY = 0; self._vx = _getVelocityProp(0, 50, true); self._vy = _getVelocityProp(0, 50, true); self.scrollX = scrollFuncX; self.scrollY = scrollFuncY; self.isDragging = self.isGesturing = self.isPressed = false; _context(this); self.enable = function (e) { if (!self.isEnabled) { _addListener(isViewport ? ownerDoc : target, "scroll", _onScroll); type.indexOf("scroll") >= 0 && _addListener(isViewport ? ownerDoc : target, "scroll", onScroll, passive, capture); type.indexOf("wheel") >= 0 && _addListener(target, "wheel", _onWheel, passive, capture); if (type.indexOf("touch") >= 0 && _isTouch || type.indexOf("pointer") >= 0) { _addListener(target, _eventTypes[0], _onPress, passive, capture); _addListener(ownerDoc, _eventTypes[2], _onRelease); _addListener(ownerDoc, _eventTypes[3], _onRelease); allowClicks && _addListener(target, "click", clickCapture, true, true); onClick && _addListener(target, "click", _onClick); onGestureStart && _addListener(ownerDoc, "gesturestart", _onGestureStart); onGestureEnd && _addListener(ownerDoc, "gestureend", _onGestureEnd); onHover && _addListener(target, _pointerType + "enter", _onHover); onHoverEnd && _addListener(target, _pointerType + "leave", _onHoverEnd); onMove && _addListener(target, _pointerType + "move", _onMove); } self.isEnabled = true; self.isDragging = self.isGesturing = self.isPressed = moved = dragged = false; self._vx.reset(); self._vy.reset(); scrollX = scrollFuncX(); scrollY = scrollFuncY(); e && e.type && _onPress(e); onEnable && onEnable(self); } return self; }; self.disable = function () { if (self.isEnabled) { // only remove the _onScroll listener if there aren't any others that rely on the functionality. _observers.filter(function (o) { return o !== self && _isViewport(o.target); }).length || _removeListener(isViewport ? ownerDoc : target, "scroll", _onScroll); if (self.isPressed) { self._vx.reset(); self._vy.reset(); _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true); } _removeListener(isViewport ? ownerDoc : target, "scroll", onScroll, capture); _removeListener(target, "wheel", _onWheel, capture); _removeListener(target, _eventTypes[0], _onPress, capture); _removeListener(ownerDoc, _eventTypes[2], _onRelease); _removeListener(ownerDoc, _eventTypes[3], _onRelease); _removeListener(target, "click", clickCapture, true); _removeListener(target, "click", _onClick); _removeListener(ownerDoc, "gesturestart", _onGestureStart); _removeListener(ownerDoc, "gestureend", _onGestureEnd); _removeListener(target, _pointerType + "enter", _onHover); _removeListener(target, _pointerType + "leave", _onHoverEnd); _removeListener(target, _pointerType + "move", _onMove); self.isEnabled = self.isPressed = self.isDragging = false; onDisable && onDisable(self); } }; self.kill = self.revert = function () { self.disable(); var i = _observers.indexOf(self); i >= 0 && _observers.splice(i, 1); _normalizer === self && (_normalizer = 0); }; _observers.push(self); isNormalizer && _isViewport(target) && (_normalizer = self); self.enable(event); }; _createClass(Observer, [{ key: "velocityX", get: function get() { return this._vx.getVelocity(); } }, { key: "velocityY", get: function get() { return this._vy.getVelocity(); } }]); return Observer; }(); Observer.version = "3.13.0"; Observer.create = function (vars) { return new Observer(vars); }; Observer.register = _initCore; Observer.getAll = function () { return _observers.slice(); }; Observer.getById = function (id) { return _observers.filter(function (o) { return o.vars.id === id; })[0]; }; _getGSAP() && gsap.registerPlugin(Observer); export { Observer as default, _isViewport, _scrollers, _getScrollFunc, _getProxyProp, _proxies, _getVelocityProp, _vertical, _horizontal, _getTarget };