vevet
Version:
Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.
486 lines • 19.3 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 __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.Snap = void 0;
var base_1 = require("../../base");
var Timeline_1 = require("../Timeline");
var Raf_1 = require("../Raf");
var utils_1 = require("../../utils");
var Slide_1 = require("./Slide");
var Wheel_1 = require("./Wheel");
var Swipe_1 = require("./Swipe");
var Track_1 = require("./Track");
var Keyboard_1 = require("./Keyboard");
var initVevet_1 = require("../../global/initVevet");
__exportStar(require("./types"), exports);
__exportStar(require("./Slide"), exports);
// todo: jsdoc
/**
* Snap/Carousel handler.
* This class manages sliding progress with options like swipe, wheel interactions, and smooth transitions.
*
* Please not that the class does not apply any styles to the slides, it only handles the logic.
*
* [Documentation](https://antonbobrov.github.io/vevet/docs/components/Snap)
*
* @group Components
*/
var Snap = /** @class */ (function (_super) {
__extends(Snap, _super);
function Snap(props) {
var _this = _super.call(this, props) || this;
/** Container size */
_this._domSize = 0;
/** All slides */
_this._slides = [];
var _a = _this.props, container = _a.container, activeIndex = _a.activeIndex;
// set vars
_this._activeIndex = activeIndex;
// add resize event
_this._resizeHandler = (0, utils_1.onResize)({
element: container,
callback: function () { return _this._handleResize(); },
name: _this.name,
});
// initial resize
_this._resizeHandler.debounceResize();
// Create the animation frame
_this._raf = new Raf_1.Raf();
_this._raf.on('frame', function () { return _this._handleRaf(); });
_this._raf.on('play', function () { return _this._callbacks.emit('rafPlay', undefined); });
_this._raf.on('pause', function () { return _this._callbacks.emit('rafPause', undefined); });
// fetch slides
_this._fetchSlides();
// add wheel listener
_this._wheel = new Wheel_1.SnapWheel(_this);
// add swipe
_this._swipe = new Swipe_1.SnapSwipe(_this);
// add track
_this._track = new Track_1.SnapTrack(_this);
// add keyboard
_this._keyboard = new Keyboard_1.SnapKeyboard(_this);
return _this;
}
/** Retrieves the default static properties. */
Snap.prototype._getStatic = function () {
return __assign(__assign({}, _super.prototype._getStatic.call(this)), { activeIndex: 0 });
};
/** Retrieves the default mutable properties. */
Snap.prototype._getMutable = function () {
return __assign(__assign({}, _super.prototype._getMutable.call(this)), { slides: false, direction: 'horizontal', centered: false, loop: false, gap: 0, lerp: 0.2, freemode: false, stickOnResize: true, friction: 0.15, edgeFriction: 0.85, duration: 500, easing: utils_1.EaseOutCubic, swipe: true, grabCursor: false, swipeSpeed: 1, swipeAxis: 'auto', followSwipe: true, shortSwipes: true, shortSwipesDuration: 300, shortSwipesThreshold: 30, swipeFriction: false, swipeLerp: (0, initVevet_1.initVevet)().mobile ? 1 : 0.6, swipeThreshold: 5, swipeMinTime: 0, wheel: false, wheelSpeed: 1, wheelAxis: 'auto', followWheel: true });
};
/** Handles properties change */
Snap.prototype._handleProps = function () {
// attach slides
this._fetchSlides();
// resize instantly
this._resizeHandler.resize();
// update props
_super.prototype._handleProps.call(this);
};
/** Update slides list and attach them */
Snap.prototype._fetchSlides = function () {
var _this = this;
this._slides.forEach(function (slide) { return slide.detach(); });
var children = this.props.slides
? this.props.slides
: Array.from(this.props.container.children);
this._slides = children.map(function (item) {
if (item instanceof Slide_1.SnapSlide) {
return item;
}
return new Slide_1.SnapSlide(item);
});
this._slides.forEach(function (slide, index) { return slide.attach(_this, index); });
};
/** Request resize (handled with debounce timeout) */
Snap.prototype.resize = function (isManual) {
if (isManual === void 0) { isManual = false; }
if (isManual) {
this._resizeHandler.resize();
}
else {
this._resizeHandler.debounceResize();
}
};
/** Resize the scene and reflow */
Snap.prototype._handleResize = function () {
var _a = this.props, direction = _a.direction, container = _a.container;
// cancel sticky behavior
this.cancelTransition();
// update container size
this._domSize =
direction === 'horizontal'
? container.offsetWidth
: container.offsetHeight;
// reflow
this._reflow();
// emit callbacks
this.callbacks.emit('resize', undefined);
};
Object.defineProperty(Snap.prototype, "container", {
/** Get container */
get: function () {
return this.props.container;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "domSize", {
/** Container size depending on direction (width or height) */
get: function () {
return this._domSize;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "slides", {
/** All slides */
get: function () {
return this._slides;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "activeIndex", {
/** Active slide index */
get: function () {
return this._activeIndex;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "activeSlide", {
/** Active slide */
get: function () {
return this.slides[this._activeIndex];
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "isEmpty", {
get: function () {
return this.slides.length === 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "axis", {
/** Get axis name depending on direction */
get: function () {
return this.props.direction === 'horizontal' ? 'x' : 'y';
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "track", {
/** Snap track */
get: function () {
return this._track;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "isTransitioning", {
/** If transition in progress */
get: function () {
return !!this._timeline;
},
enumerable: false,
configurable: true
});
/** Reflow: update static values of slides */
Snap.prototype._reflow = function () {
var _this = this;
var _a = this, slides = _a.slides, props = _a.props;
if (slides.length === 0) {
return;
}
// Calculate static values
slides.reduce(function (prev, slide) {
slide.setStaticCoord(prev);
return prev + slide.size + (0, utils_1.toPixels)(props.gap);
}, 0);
// Reset to active slide
var slide = slides.find(function (_a) {
var index = _a.index;
return index === _this.activeIndex;
});
if (props.stickOnResize && slide) {
this._track.set(slide.staticCoord);
}
else {
this._track.clampTarget();
}
// Emit callbacks
this.callbacks.emit('reflow', undefined);
// Render after resize
this._render();
};
/** Handle RAF update, interpolate track values */
Snap.prototype._handleRaf = function () {
if (this.isTransitioning) {
return;
}
var _a = this, track = _a._track, props = _a.props, swipe = _a._swipe;
// Get lerp factor
var lerpFactor = swipe.isSwiping && props.swipeLerp ? props.swipeLerp : props.lerp;
// Interpolate track value
track.lerp(this._raf.lerpFactor(lerpFactor));
// Stop raf if target reached
if (track.isInterpolated) {
this._raf.pause();
}
// Render the scene
this._render(this._raf.duration);
};
/** Render slides logic */
Snap.prototype._render = function (frameDuration) {
if (frameDuration === void 0) { frameDuration = 0; }
if (this.isEmpty) {
return;
}
var _a = this, track = _a._track, props = _a.props;
// Update values
this._updateSlidesCoords();
this._updateSlidesProgress();
// Get magnet after slide coordinates are updated
var _b = this, magnet = _b.magnet, swipe = _b._swipe;
if (!magnet) {
return;
}
// Active index change
if (magnet.slide.index !== this._activeIndex) {
this._activeIndex = magnet.slide.index;
this.callbacks.emit('activeSlide', this.activeSlide);
}
// Check if friction is allowed
var canHaveFriction = (swipe.isSwiping && swipe.allowFriction) || !swipe.isSwiping;
// Apply friction
if (!track.isSlideScrolling &&
!props.freemode &&
canHaveFriction &&
frameDuration > 0 &&
props.friction >= 0) {
track.target = (0, utils_1.damp)(track.target, track.current + magnet.magnetDiff, props.friction * props.lerp, frameDuration, 0.000001);
}
// Render slides
this.slides.forEach(function (slide) { return slide.render(); });
// Emit Calbacks
this.callbacks.emit('update', undefined);
};
/** Update slides values */
Snap.prototype._updateSlidesCoords = function () {
var _a = this, slides = _a.slides, domSize = _a.domSize, track = _a._track;
var isCentered = this.props.centered;
slides.forEach(function (slide) {
var staticCoord = slide.staticCoord, size = slide.size;
var offset = isCentered ? domSize / 2 - slides[0].size / 2 : 0;
if (!track.canLoop) {
slide.setCoord(staticCoord + offset - track.current);
return;
}
if (isCentered) {
slide.setCoord((0, utils_1.loop)(staticCoord + offset - track.current, -track.max / 2 + offset, track.max / 2 + offset));
return;
}
slide.setCoord((0, utils_1.loop)(staticCoord - track.current, -size, track.max - size));
});
};
/** Update slides progress */
Snap.prototype._updateSlidesProgress = function () {
var _this = this;
var _a = this, slides = _a.slides, domSize = _a.domSize;
slides.forEach(function (slide) {
var coord = slide.coord, size = slide.size;
if (_this.props.centered) {
var center = domSize / 2 - size / 2;
slide.setProgress((0, utils_1.scoped)(coord, center, center - size));
return;
}
slide.setProgress((0, utils_1.scoped)(coord, 0, -size));
});
};
Object.defineProperty(Snap.prototype, "magnets", {
/** Get slide magnets */
get: function () {
var _this = this;
var _a = this, domSize = _a.domSize, props = _a.props, track = _a.track;
var threshold = props.centered ? domSize / 2 : 0;
var slideMagnets = this.slides.map(function (slide) {
var firstPoint = props.centered
? slide.coord + slide.size / 2
: slide.coord;
var points = [firstPoint];
if (slide.size > domSize) {
if (props.centered) {
points.push(firstPoint + (domSize - slide.size) / 2);
points.push(firstPoint - (domSize - slide.size) / 2);
}
else {
points.push(firstPoint + slide.size - domSize);
}
}
return points.map(function (point) { return ({
slide: slide,
magnetCoord: point,
magnetDiff: point - threshold,
staticMagnetCoord: point + _this._track.current,
}); });
});
var flatMagnets = slideMagnets.flat();
if (!track.canLoop && !props.centered) {
flatMagnets.forEach(function (magnet) {
var oldStaticMagnetCoord = magnet.staticMagnetCoord;
var newStaticMagnetCoord = (0, utils_1.clamp)(magnet.staticMagnetCoord, 0, _this._track.max);
var diff = oldStaticMagnetCoord - newStaticMagnetCoord;
magnet.staticMagnetCoord = newStaticMagnetCoord;
magnet.magnetCoord -= diff;
magnet.magnetDiff -= diff;
});
}
return flatMagnets;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Snap.prototype, "magnet", {
/** Get nearest magnet */
get: function () {
if (this.isEmpty) {
return undefined;
}
var magnets = this.magnets.sort(function (a, b) { return Math.abs(a.magnetDiff) - Math.abs(b.magnetDiff); });
return magnets[0];
},
enumerable: false,
configurable: true
});
/** Cancel sticky behavior */
Snap.prototype.cancelTransition = function () {
var _a;
(_a = this._timeline) === null || _a === void 0 ? void 0 : _a.destroy();
this._timeline = undefined;
};
/** Stick to the nearest magnet */
Snap.prototype.stick = function () {
var magnet = this.magnet;
if (this.track.isSlideScrolling || !magnet) {
return;
}
this.toCoord(this._track.current + magnet.magnetDiff);
};
/** Go to a definite coordinate */
Snap.prototype.toCoord = function (coordinate, duration) {
var _this = this;
if (duration === void 0) { duration = this.props.duration; }
if (this.isEmpty) {
return;
}
this.cancelTransition();
var start = this._track.current;
var end = coordinate;
var diff = Math.abs(end - start);
var tm = new Timeline_1.Timeline({
duration: typeof duration === 'number' ? duration : duration(diff),
easing: this.props.easing,
});
this._timeline = tm;
tm.on('start', function () { return _this.callbacks.emit('timelineStart', undefined); });
tm.on('update', function (data) {
_this._track.current = (0, utils_1.lerp)(start, end, data.eased);
_this._track.target = _this._track.current;
_this._render();
_this.callbacks.emit('timelineUpdate', data);
});
tm.on('end', function () {
tm.destroy();
_this.callbacks.emit('timelineEnd', undefined);
_this._timeline = undefined;
});
tm.play();
};
/** Go to a slide by index */
Snap.prototype.toSlide = function (index, duration) {
if (duration === void 0) { duration = this.props.duration; }
if (index === this.activeIndex) {
this.stick();
return;
}
var magnet = this.magnets.find(function (data) { return data.slide.index === index; });
if (!magnet) {
return;
}
this.toCoord(this._track.current + magnet.magnetDiff, duration);
};
/** Go to next slide */
Snap.prototype.next = function (duration) {
if (duration === void 0) { duration = this.props.duration; }
var _a = this, props = _a.props, slides = _a.slides, activeIndex = _a.activeIndex;
var index = props.loop
? (0, utils_1.loop)(activeIndex + 1, 0, slides.length)
: Math.min(activeIndex + 1, slides.length - 1);
this.toSlide(index, duration);
};
/** Go to previous slide */
Snap.prototype.prev = function (duration) {
if (duration === void 0) { duration = this.props.duration; }
var _a = this, props = _a.props, slides = _a.slides, activeIndex = _a.activeIndex;
var index = props.loop
? (0, utils_1.loop)(activeIndex - 1, 0, slides.length)
: Math.max(activeIndex - 1, 0);
this.toSlide(index, duration);
};
/**
* Destroys the component and clears all timeouts and resources.
*/
Snap.prototype._destroy = function () {
_super.prototype._destroy.call(this);
this._resizeHandler.remove();
this.cancelTransition();
this._raf.destroy();
this._slides.forEach(function (slide) { return slide.detach(); });
};
return Snap;
}(base_1.Module));
exports.Snap = Snap;
//# sourceMappingURL=index.js.map