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
JavaScript
"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