UNPKG

vevet

Version:

Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.

374 lines 15.2 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Marquee = void 0; var Module_1 = require("../../base/Module"); var initVevet_1 = require("../../global/initVevet"); var isFiniteNumber_1 = require("../../internal/isFiniteNumber"); var noopIfDestroyed_1 = require("../../internal/noopIfDestroyed"); var textDirection_1 = require("../../internal/textDirection"); var utils_1 = require("../../utils"); var listeners_1 = require("../../utils/listeners"); var math_1 = require("../../utils/math"); var Raf_1 = require("../Raf"); var Nodes_1 = require("./Nodes"); var props_1 = require("./props"); var styles_1 = require("./styles"); __exportStar(require("./types"), exports); /** * A custom marquee component that smoothly scrolls its child elements. * * This component is designed to loop elements horizontally within a container, * with support for customization such as speed, gap, pause on hover, and more. * * [Documentation](https://vevetjs.com/docs/Marquee) * * @group Components */ var Marquee = /** @class */ (function (_super) { __extends(Marquee, _super); function Marquee(props, onCallbacks) { var _this = _super.call(this, props, onCallbacks) || this; /** Current container size (width or height depending on direction) */ _this._containerSize = 0; /** Array of sizes of each child element */ _this._sizes = []; /** Total size of all elements in the marquee */ _this._totalSize = 0; /** The current marquee coordinate. */ _this._coord = 0; /** Detects if the container is RTL */ _this._isRtl = false; var _a = _this.props, container = _a.container, direction = _a.direction; var isVertical = _this.isVertical; if (!container) { throw new Error('Marquee container is not defined'); } // Update direction var isRtl = (0, textDirection_1.getTextDirection)(container) === 'rtl' && direction === 'horizontal'; _this._isRtl = isRtl; // Apply base styles to the container (0, styles_1.appleMarqueeContainerStyles)({ container: container, isVertical: isVertical, isRtl: isRtl }); // Create nodes manager _this._nodes = new Nodes_1.MarqueeNodes(_this); // Setup elements in the marquee _this._setup(); // Create animation frame _this._raf = new Raf_1.Raf({ enabled: _this.props.enabled, fpsRecalcFrames: 1 }); // Set events _this._setEvents(); return _this; } /** Get default static properties. */ Marquee.prototype._getStatic = function () { return __assign(__assign({}, _super.prototype._getStatic.call(this)), props_1.STATIC_PROPS); }; /** Get default mutable properties. */ Marquee.prototype._getMutable = function () { return __assign(__assign({}, _super.prototype._getMutable.call(this)), props_1.MUTABLE_PROPS); }; Object.defineProperty(Marquee.prototype, "totalSize", { /** Total size of all elements in the marquee (width or height depending on direction) */ get: function () { return this._totalSize; }, enumerable: false, configurable: true }); Object.defineProperty(Marquee.prototype, "totalWidth", { /** * Total width of all elements in the marquee * @deprecated Use `totalSize` instead */ get: function () { return this.totalSize; }, enumerable: false, configurable: true }); Object.defineProperty(Marquee.prototype, "coord", { /** The current marquee coordinate. */ get: function () { return this._coord; }, set: function (value) { this._coord = value; this.render(0); }, enumerable: false, configurable: true }); Object.defineProperty(Marquee.prototype, "x", { /** * The current coordinate of the marquee. * @deprecated Use `coord` instead */ get: function () { return this.coord; }, set: function (value) { this.coord = value; }, enumerable: false, configurable: true }); Object.defineProperty(Marquee.prototype, "isVertical", { /** Check if the marquee is vertical */ get: function () { return this.props.direction === 'vertical'; }, enumerable: false, configurable: true }); Object.defineProperty(Marquee.prototype, "gap", { /** Marquee gap */ get: function () { return Math.max((0, utils_1.toPixels)(this.props.gap), 0); }, enumerable: false, configurable: true }); /** Handles property changes */ Marquee.prototype._handleProps = function (props) { _super.prototype._handleProps.call(this, props); if (this.props.enabled) { this._raf.play(); } else { this._raf.pause(); } // Rerender the elements this.resize(); this.render(0); }; /** Set marquee events */ Marquee.prototype._setEvents = function () { var _this = this; var container = this.props.container; this._raf.on('frame', function () { var props = _this.props; var factor = props.adjustSpeed ? _this._raf.fpsFactor : 1; var speed = (0, utils_1.toPixels)(props.speed); _this._render(speed * factor); }); // Pause on hover var mouseenter = (0, listeners_1.addEventListener)(container, 'mouseenter', function () { if (_this.props.pauseOnHover) { _this._raf.pause(); } }); // Resume on mouse leave var mouseleave = (0, listeners_1.addEventListener)(container, 'mouseleave', function () { if (_this.props.enabled) { _this._raf.play(); } }); // Intersection observer var intersection = new IntersectionObserver(this._handleIntersection.bind(this), { root: null }); intersection.observe(container); this.onDestroy(function () { mouseenter(); mouseleave(); intersection.disconnect(); }); }; /** Initializes the marquee setup, including resizing and cloning elements */ Marquee.prototype._setup = function () { var _this = this; var _a; (_a = this._lastSetup) === null || _a === void 0 ? void 0 : _a.call(this); if (this.isDestroyed) { return; } var _b = this.props, container = _b.container, resizeDebounce = _b.resizeDebounce; // Process nodes this._nodes.save(); this._nodes.wrap(); this._nodes.applyStyles(); // initial resize this.resize(); // Resize on page load var onPageLoad = (0, initVevet_1.initVevet)().onLoad(function () { return _this.resize(); }); // Handle resizing var resizeHandler = (0, listeners_1.onResize)({ callback: function () { return _this.resize(); }, element: __spreadArray([container], this._nodes.elements, true), viewportTarget: 'width', resizeDebounce: resizeDebounce, name: this.name, }); // Setup cleanup function this._lastSetup = function () { onPageLoad(); resizeHandler.remove(); }; }; /** Resizes the marquee, recalculating element positions and cloning if necessary. */ Marquee.prototype.resize = function () { var _this = this; var _a = this, props = _a.props, isVertical = _a.isVertical, gap = _a.gap; var container = props.container; // Update container width var containerSize = isVertical ? container.offsetHeight : container.offsetWidth; this._containerSize = containerSize; // Update element sizes this._sizes = this._nodes.elements.map(function (el) { return (isVertical ? el.offsetHeight : el.offsetWidth) + gap; }); this._totalSize = this._sizes.reduce(function (a, b) { return a + b; }, 0); // Determine how many times to duplicate elements var maxSize = Math.max.apply(Math, this._sizes); var copyTimes = Math.ceil((containerSize + maxSize) / this._totalSize); // update total size this._totalSize = Math.max(this._totalSize, containerSize + maxSize); // Clone elements if necessary if (props.cloneNodes && (0, isFiniteNumber_1.isFiniteNumber)(copyTimes) && copyTimes > 1) { this._nodes.cloneAll(copyTimes - 1); this.resize(); } // Trigger resize callbacks this.callbacks.emit('resize', undefined); // Rerender the marquee setTimeout(function () { return _this.render(0); }, 0); }; /** Renders the marquee, adjusting element positions. */ Marquee.prototype.render = function (step) { this._render(step); }; /** * Renders the marquee, calculating element positions based on the provided speed. */ Marquee.prototype._render = function (stepProp) { if (stepProp === void 0) { stepProp = this.props.speed; } if (this.isDestroyed) { return; } var _a = this, isVertical = _a.isVertical, props = _a.props, gap = _a.gap; var elements = this._nodes.elements; // Calculate step var rawStep = this._isRtl ? -stepProp : stepProp; var step = (0, utils_1.toPixels)(rawStep); if (!(0, isFiniteNumber_1.isFiniteNumber)(step)) { return; } // Update animation time this._coord -= step; // Calculate current position of the elements var centerCoord = this._containerSize * 0.5 + this._sizes[0] / 2 - gap; var position = this._coord + (props.centered ? centerCoord : 0); // Update each element's position var prevStaticCoord = 0; for (var index = 0; index < elements.length; index += 1) { var element = elements[index]; var elementSize = this._sizes[index]; var style = element.style; var coord = (0, math_1.loop)(position + prevStaticCoord, -elementSize, this._totalSize - elementSize); // Apply transformations to position the element if (isVertical) { var x = style.position === 'relative' ? '0' : '-50%'; style.transform = "translate(".concat(x, ", ").concat(coord, "px)"); } else { var y = style.position === 'relative' ? '0' : '-50%'; style.transform = "translate(".concat(coord, "px, ").concat(y, ")"); } prevStaticCoord += elementSize; } // Trigger render callbacks this.callbacks.emit('render', undefined); }; /** Handle intersection observer */ Marquee.prototype._handleIntersection = function (entries) { var _this = this; if (!this.props.pauseOnOut) { return; } entries.forEach(function (entry) { if (entry.isIntersecting && _this.props.enabled) { _this._raf.play(); } else { _this._raf.pause(); } }); }; /** Destroys the instance and cleans up resources */ Marquee.prototype._destroy = function () { var _a; var container = this.props.container; _super.prototype._destroy.call(this); this._raf.destroy(); (_a = this._lastSetup) === null || _a === void 0 ? void 0 : _a.call(this); // Remove all children and restore the initial nodes while (container.firstChild) { container.removeChild(container.firstChild); } // Restore the initial nodes this._nodes.destroy(); // Restore container style (0, styles_1.removeMarqueeContainerStyles)(container); }; __decorate([ noopIfDestroyed_1.noopIfDestroyed ], Marquee.prototype, "resize", null); __decorate([ noopIfDestroyed_1.noopIfDestroyed ], Marquee.prototype, "render", null); return Marquee; }(Module_1.Module)); exports.Marquee = Marquee; //# sourceMappingURL=index.js.map