vevet
Version:
Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.
589 lines • 24.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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cursor = void 0;
var Module_1 = require("../../base/Module");
var initVevet_1 = require("../../global/initVevet");
var cn_1 = require("../../internal/cn");
var env_1 = require("../../internal/env");
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 constants_1 = require("./constants");
var HoverElement_1 = require("./HoverElement");
var Path_1 = require("./Path");
var props_1 = require("./props");
var styles_1 = require("./styles");
__exportStar(require("./types"), exports);
/**
* A customizable custom cursor component with smooth animations and hover interactions.
* Supports dynamic appearance changes and enhanced user interaction.
*
* [Documentation](https://vevetjs.com/docs/Cursor)
*
* @group Components
*/
var Cursor = /** @class */ (function (_super) {
__extends(Cursor, _super);
function Cursor(props, onCallbacks) {
var _this = _super.call(this, props, onCallbacks) || this;
/** Attached hover elements */
_this._elements = [];
/** Active hovered element */
_this._activeElements = [];
/** Defines if the cursor has been moved after initialization */
_this._isFirstMove = true;
var isEnabled = _this.props.enabled;
var _a = _this, initialWidth = _a.initialWidth, initialHeight = _a.initialHeight;
// Set default variables
_this._coords = {
x: 0,
y: 0,
width: initialWidth,
height: initialHeight,
angle: 0,
velocity: 0,
};
_this._rawTarget = __assign({}, _this._coords);
_this._types = [];
_this._activeTypes = [];
// Create cursor path
_this._path = new Path_1.CursorPath(_this.hasPath);
// No need to remove styles on destroy
(0, styles_1.createCursorStyles)(_this.prefix);
// Setup
_this._setClassNames();
_this._createElements();
_this._setEvents();
// enable by default
_this._toggle(isEnabled);
return _this;
}
/** Get default static properties */
Cursor.prototype._getStatic = function () {
return __assign(__assign({}, _super.prototype._getStatic.call(this)), props_1.STATIC_PROPS);
};
/** Get default mutable properties */
Cursor.prototype._getMutable = function () {
return __assign(__assign({}, _super.prototype._getMutable.call(this)), props_1.MUTABLE_PROPS);
};
Object.defineProperty(Cursor.prototype, "prefix", {
/**
* Classname prefix for styling elements.
*/
get: function () {
return "".concat((0, initVevet_1.initVevet)().prefix, "cursor");
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "container", {
/** The cursor container */
get: function () {
return this.props.container;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "domContainer", {
/** Returns the DOM parent for the cursor element. */
get: function () {
if (this.container instanceof Window) {
return env_1.body;
}
return this.container;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "outer", {
/**
* The outer element of the custom cursor.
* This is the visual element that represents the cursor on screen.
*/
get: function () {
return this._outer;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "inner", {
/**
* The inner element of the custom cursor.
* This element is nested inside the outer element and can provide additional styling.
*/
get: function () {
return this._inner;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "initialWidth", {
/** Cursor initial width */
get: function () {
return (0, utils_1.toPixels)(this.props.width);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "initialHeight", {
/** Cursor initial width */
get: function () {
return (0, utils_1.toPixels)(this.props.height);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "coords", {
/**
* The current coordinates (x, y, width, height).
* These are updated during cursor movement.
*/
get: function () {
return this._coords;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "hoveredElement", {
/**
* The currently hovered element.
* Stores information about the element that the cursor is currently interacting with.
*/
get: function () {
var activeElements = this._activeElements;
return activeElements[activeElements.length - 1];
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "targetCoords", {
/** Target coordinates of the cursor (without smooth interpolation). */
get: function () {
var _a, _b, _c, _d;
var _e = this, hoveredElement = _e.hoveredElement, initialWidth = _e.initialWidth, initialHeight = _e.initialHeight;
var _f = this._rawTarget, x = _f.x, y = _f.y;
var _g = this._rawTarget, angle = _g.angle, velocity = _g.velocity;
var width = initialWidth;
var height = initialHeight;
var padding = 0;
if (hoveredElement) {
var dimensions = hoveredElement.getDimensions();
width = (_a = dimensions.width) !== null && _a !== void 0 ? _a : initialWidth;
height = (_b = dimensions.height) !== null && _b !== void 0 ? _b : initialHeight;
x = (_c = dimensions.x) !== null && _c !== void 0 ? _c : x;
y = (_d = dimensions.y) !== null && _d !== void 0 ? _d : y;
padding = dimensions.padding;
}
width += padding * 2;
height += padding * 2;
return { x: x, y: y, width: width, height: height, angle: angle, velocity: velocity };
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "path", {
/** Returns an SVG path element which represents the cursor movement */
get: function () {
return this._path.path;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Cursor.prototype, "hasPath", {
/** Check if the cursor has a path */
get: function () {
return this.props.behavior === 'path';
},
enumerable: false,
configurable: true
});
/** Handles property mutations */
Cursor.prototype._handleProps = function (props) {
_super.prototype._handleProps.call(this, props);
this._toggle(this.props.enabled);
};
/** Sets class names */
Cursor.prototype._setClassNames = function () {
var domContainer = this.domContainer;
// Hide native cursor
if (this.props.hideNative) {
domContainer.style.cursor = 'none';
this._addTempClassName(domContainer, this._cn('-hide-default'));
}
// Set class names
this._addTempClassName(domContainer, this._cn('-container'));
// Set container position
if (domContainer !== env_1.body) {
domContainer.style.position = 'relative';
}
// Reset styles
this.onDestroy(function () {
domContainer.style.cursor = '';
});
};
/** Creates the custom cursor and appends it to the DOM. */
Cursor.prototype._createElements = function () {
var _a = this, container = _a.container, domContainer = _a.domContainer, props = _a.props;
var isWindow = container instanceof Window;
var cn = this._cn.bind(this);
// Create outer element
var outer = env_1.doc.createElement('div');
(0, cn_1.cnAdd)(outer, cn(''));
(0, cn_1.cnAdd)(outer, cn(isWindow ? '-in-window' : '-in-element'));
(0, cn_1.cnAdd)(outer, cn('-disabled'));
// Append the outer element to the DOM container
if (props.append) {
domContainer.append(outer);
}
// set direction
var direction = (0, textDirection_1.getTextDirection)(outer);
(0, cn_1.cnAdd)(outer, cn("_".concat(direction)));
// Create inner element
var inner = env_1.doc.createElement('div');
outer.append(inner);
(0, cn_1.cnAdd)(inner, cn('__inner'));
(0, cn_1.cnAdd)(inner, cn('-disabled'));
outer.append(inner);
// assign
this._outer = outer;
this._inner = inner;
// Destroy the cursor
this.onDestroy(function () {
inner.remove();
outer.remove();
});
};
/** Sets up the various event listeners for the cursor, such as mouse movements and clicks. */
Cursor.prototype._setEvents = function () {
var _this = this;
var domContainer = this.domContainer;
this._raf = new Raf_1.Raf({ enabled: false });
this._raf.on('frame', function () { return _this.render(); });
var mouseenter = (0, listeners_1.addEventListener)(domContainer, 'mouseenter', this._handleMouseEnter.bind(this));
var mouseleave = (0, listeners_1.addEventListener)(domContainer, 'mouseleave', this._handleMouseLeave.bind(this));
var mousemove = (0, listeners_1.addEventListener)(domContainer, 'mousemove', this._handleMouseMove.bind(this));
var mousedown = (0, listeners_1.addEventListener)(domContainer, 'mousedown', this._handleMouseDown.bind(this));
var mouseup = (0, listeners_1.addEventListener)(domContainer, 'mouseup', this._handleMouseUp.bind(this));
var blur = (0, listeners_1.addEventListener)(window, 'blur', this._handleWindowBlur.bind(this));
this.onDestroy(function () {
var _a;
(_a = _this._raf) === null || _a === void 0 ? void 0 : _a.destroy();
mouseenter();
mouseleave();
mousemove();
mousedown();
mouseup();
blur();
});
};
/** Enables cursor animation. */
Cursor.prototype._toggle = function (enabled) {
var _a;
var className = this._cn('-disabled');
(0, cn_1.cnToggle)(this.outer, className, !enabled);
(0, cn_1.cnToggle)(this.inner, className, !enabled);
(_a = this._raf) === null || _a === void 0 ? void 0 : _a.updateProps({ enabled: enabled });
};
/** Handles mouse enter events. */
Cursor.prototype._handleMouseEnter = function (evt) {
if (!this.props.enabled) {
return;
}
var x = evt.clientX, y = evt.clientY;
var target = this._rawTarget;
this._coords.x = x;
this._coords.y = y;
target.x = x;
target.y = y;
this._path.addPoint(target, true);
(0, cn_1.cnAdd)(this.outer, this._cn('-visible'));
};
/** Handles mouse leave events. */
Cursor.prototype._handleMouseLeave = function () {
(0, cn_1.cnRemove)(this.outer, this._cn('-visible'));
};
/** Handles mouse move events. */
Cursor.prototype._handleMouseMove = function (evt) {
var _a;
if (!this.props.enabled) {
return;
}
var x = evt.clientX, y = evt.clientY;
var target = this._rawTarget;
var prevX = target.x, prevY = target.y;
// Calculate angle
var deltaX = prevX - this._coords.x;
var deltaY = prevY - this._coords.y;
var prevAngle = target.angle;
var rawAngle = (Math.atan2(deltaY, deltaX) * 180) / Math.PI;
var targetAngle = prevAngle + ((((rawAngle - prevAngle) % 360) + 540) % 360) - 180;
// Calculate velocity
var velocity = Math.min(Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) * 2, 150) / 150;
// Update target coordinates
target.x = x;
target.y = y;
target.angle = targetAngle;
target.velocity = velocity;
// Update interpolated coords if first move
if (this._isFirstMove) {
this._coords.x = target.x;
this._coords.y = target.y;
this._coords.angle = target.angle;
this._coords.velocity = target.velocity;
this._isFirstMove = false;
}
// Add path point
this._path.addPoint(target);
// Handle classnames
(0, cn_1.cnAdd)(this.outer, this._cn('-visible'));
// Enable animation
(_a = this._raf) === null || _a === void 0 ? void 0 : _a.play();
};
/** Handles mouse down events. */
Cursor.prototype._handleMouseDown = function (evt) {
var className = this._cn('-click');
if (evt.which === 1) {
(0, cn_1.cnAdd)(this.outer, className);
(0, cn_1.cnAdd)(this.inner, className);
}
};
/** Handles mouse up events. */
Cursor.prototype._handleMouseUp = function () {
var className = this._cn('-click');
(0, cn_1.cnRemove)(this.outer, className);
(0, cn_1.cnRemove)(this.inner, className);
};
/** Handles window blur events. */
Cursor.prototype._handleWindowBlur = function () {
this._handleMouseUp();
};
/**
* Registers an element to interact with the cursor, enabling dynamic size and position changes based on hover effects.
* @returns Returns a destructor
*/
Cursor.prototype.attachHover = function (settings) {
var _this = this;
var element = new HoverElement_1.CursorHoverElement(settings, function (data) { return _this._handleElementEnter(data); }, function (data) { return _this._handleElementLeave(data); });
this._elements.push(element);
var destroy = function () {
_this._elements = _this._elements.filter(function (i) { return i !== element; });
element.destroy();
};
this.onDestroy(function () { return destroy(); });
return function () { return destroy(); };
};
/** Handle element mouse enter event */
Cursor.prototype._handleElementEnter = function (data) {
var _a;
if (!this.props.enabled) {
return;
}
this._activeElements.push(data);
if (data.type) {
this._toggleType(data.type, true);
}
this.callbacks.emit('hoverEnter', data);
(_a = this._raf) === null || _a === void 0 ? void 0 : _a.play();
};
/** Handle element mouse leave event */
Cursor.prototype._handleElementLeave = function (data) {
var _a;
this._activeElements = this._activeElements.filter(function (i) { return i !== data; });
if (data.type) {
this._toggleType(data.type, false);
}
this.callbacks.emit('hoverLeave', data);
if (this.props.enabled) {
(_a = this._raf) === null || _a === void 0 ? void 0 : _a.play();
}
};
/**
* Registers a cursor type.
*/
Cursor.prototype.attachCursor = function (_a) {
var _b;
var element = _a.element, type = _a.type;
this._types.push({ element: element, type: type });
(_b = this._inner) === null || _b === void 0 ? void 0 : _b.append(element);
};
/** Enable or disable a cursor type */
Cursor.prototype._toggleType = function (type, isEnabled) {
var targetType = this._types.find(function (item) { return item.type === type; });
if (isEnabled) {
this._activeTypes.push(type);
}
else {
this._activeTypes = this._activeTypes.filter(function (item) { return type !== item; });
}
var activeTypes = this._activeTypes;
var activeType = activeTypes.length > 0 ? activeTypes[activeTypes.length - 1] : null;
this._types.forEach(function (item) {
(0, cn_1.cnToggle)(item.element, 'active', item.type === activeType);
});
if (targetType) {
this.callbacks.emit(isEnabled ? 'typeShow' : 'typeHide', targetType);
}
if (!activeType) {
this.callbacks.emit('noType', undefined);
}
};
Object.defineProperty(Cursor.prototype, "isInterpolated", {
/**
* Checks if all coordinates are interpolated.
* @returns {boolean} True if all coordinates are interpolated, false otherwise.
*/
get: function () {
var _a = this, coords = _a.coords, targetCoords = _a.targetCoords, props = _a.props;
var isWidthDone = coords.width === targetCoords.width;
var isHeightDone = coords.height === targetCoords.height;
var isAngleDone = coords.angle === targetCoords.angle;
var isVelocityDone = coords.velocity === targetCoords.velocity;
var isElementsDone = !this._elements.find(function (element) { return !element.isInterpolated; });
var isPathDone = this._path.isInterpolated;
var isCoordsDone = coords.x === targetCoords.x && coords.y === targetCoords.y;
return (isWidthDone &&
isHeightDone &&
isAngleDone &&
isVelocityDone &&
isElementsDone &&
(props.behavior === 'path' ? isPathDone : isCoordsDone));
},
enumerable: false,
configurable: true
});
/** Renders the cursor. */
Cursor.prototype.render = function () {
var _a;
this._calculate();
this._renderElements();
if (this.props.autoStop && this.isInterpolated) {
(_a = this._raf) === null || _a === void 0 ? void 0 : _a.pause();
}
// Launch render events
this.callbacks.emit('render', undefined);
};
/** Recalculates current coordinates. */
Cursor.prototype._calculate = function () {
var _a = this, target = _a.targetCoords, coords = _a._coords;
var lerpFactor = this._getLerpFactor();
this._path.lerp(lerpFactor);
this._path.minimize();
try {
if (this.hasPath) {
var pathCoord = this._path.coord;
coords.x = pathCoord.x;
coords.y = pathCoord.y;
}
else {
throw new Error('No path');
}
}
catch (_b) {
coords.x = this._lerp(coords.x, target.x);
coords.y = this._lerp(coords.y, target.y);
}
coords.width = this._lerp(coords.width, target.width);
coords.height = this._lerp(coords.height, target.height);
coords.angle = this._lerp(coords.angle, target.angle);
this._rawTarget.velocity = this._lerp(this._rawTarget.velocity, 0);
coords.velocity = this._lerp(coords.velocity, this._rawTarget.velocity);
};
/** Gets the interpolation factor. */
Cursor.prototype._getLerpFactor = function (input) {
if (input === void 0) { input = this.props.lerp; }
if (!(0, isFiniteNumber_1.isFiniteNumber)(input)) {
return 1;
}
var lerpFactor = (0, math_1.clamp)(input, 0, 1);
return this._raf.lerpFactor(lerpFactor);
};
/** Performs linear interpolation. */
Cursor.prototype._lerp = function (current, target) {
var lerpFactor = this._getLerpFactor();
var value = (0, math_1.lerp)(current, target, lerpFactor, constants_1.LERP_APPROXIMATION);
return value;
};
/** Renders the cursor elements. */
Cursor.prototype._renderElements = function () {
var _this = this;
var _a = this, container = _a.container, domContainer = _a.domContainer, outer = _a.outer, props = _a.props, coords = _a.coords;
var width = coords.width, height = coords.height;
var x = coords.x, y = coords.y;
if (!(container instanceof Window)) {
var bounding = domContainer.getBoundingClientRect();
x -= bounding.left;
y -= bounding.top;
}
// Update DOM coordinates
var style = outer.style;
style.setProperty('--cursor-w', "".concat(width, "px"));
style.setProperty('--cursor-h', "".concat(height, "px"));
style.transform = props.transformModifier(__assign(__assign({}, coords), { x: x, y: y }));
// Render element
this._elements.forEach(function (element) {
return element.render(_this._getLerpFactor.bind(_this));
});
};
__decorate([
noopIfDestroyed_1.noopIfDestroyed
], Cursor.prototype, "attachHover", null);
__decorate([
noopIfDestroyed_1.noopIfDestroyed
], Cursor.prototype, "attachCursor", null);
__decorate([
noopIfDestroyed_1.noopIfDestroyed
], Cursor.prototype, "render", null);
return Cursor;
}(Module_1.Module));
exports.Cursor = Cursor;
//# sourceMappingURL=index.js.map