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.

169 lines 5.5 kB
import { clamp, inRange, lerp, loop, scoped } from '../../../utils/math'; import { toPixels } from '../../../utils'; export class SnapTrack { constructor(snap) { this.snap = snap; /** The current track value */ this._current = 0; /** The target track value */ this._target = 0; } /** Gets the current track value. */ get current() { return this._current; } /** Sets the current track value */ set current(value) { this._current = value; } /** Gets the target track value. */ get target() { return this._target; } /** Sets the target track value */ set target(value) { this._target = value; } /** Set a value to current & target value instantly */ set(value) { this.current = value; this.target = value; } /** If can loop */ get canLoop() { return this.snap.props.loop && this.snap.slides.length > 1; } /** Get looped current value */ get loopedCurrent() { return this.loopCoord(this.current); } /** Get track offset */ get offset() { const { snap } = this; return snap.props.centered ? snap.domSize / 2 - snap.firstSlideSize / 2 : 0; } /** Get loop count */ get loopCount() { return Math.floor(this.current / this.max); } /** Loop a coordinate if can loop */ loopCoord(coord) { return this.canLoop ? loop(coord, this.min, this.max) : coord; } /** Interpolate the current track value */ lerp(factor) { let { target } = this; const { snap, min, max } = this; // Edge space & resistance if (!snap.props.loop) { const { domSize, props } = snap; const edgeSpace = (1 - props.edgeFriction) * domSize; if (target < min) { const edgeProgress = 1 - scoped(target, -domSize, min); target = min - edgeProgress * edgeSpace; } else if (target > max) { const edgeProgress = scoped(target, max, max + domSize); target = max + edgeProgress * edgeSpace; } target = clamp(target, min - edgeSpace, max + edgeSpace); } // Interpolate current value const rest = Math.abs(this.current - target); const fastThreshold = 5; if (rest < fastThreshold) { const fastProgress = 1 - rest / fastThreshold; const additionalFactor = (1 - factor) / 3; factor += additionalFactor * fastProgress; } this.current = lerp(this.current, target, factor, 0.000001); } /** Whether the track is interpolated */ get isInterpolated() { return this.current === this.target; } /** Get minimum track value */ get min() { const { snap } = this; if (this.canLoop || snap.isEmpty) { return 0; } if (snap.props.centered) { const firstSlide = snap.slides[0]; if (firstSlide.size > snap.domSize) { return snap.domSize / 2 - firstSlide.size / 2; } } return 0; } /** Get maximum track value */ get max() { const { domSize, slides, isEmpty, props } = this.snap; const { canLoop } = this; if (isEmpty) { return 0; } const firstSlide = slides[0]; const lastSlide = slides[slides.length - 1]; const lastCoordWithSlide = lastSlide.staticCoord + lastSlide.size; let max = canLoop ? lastCoordWithSlide + toPixels(props.gap) : lastCoordWithSlide - domSize; if (canLoop) { return max; } if (props.centered) { max += domSize / 2 - firstSlide.size / 2; if (lastSlide.size < domSize) { max += domSize / 2 - lastSlide.size / 2; } } if (!props.centered) { max = Math.max(max, 0); } return max; } /** Get track progress. From 0 to 1 if not loop. From -Infinity to Infinity if loop */ get progress() { return this.current / this.max; } /** Iterate track target value */ iterateTarget(delta) { const { snap } = this; this.target += delta; // @ts-ignore // eslint-disable-next-line no-underscore-dangle snap._raf.play(); } /** Clamp target value between min and max values */ clampTarget() { const { snap } = this; if (!this.canLoop) { this.target = clamp(this.target, this.min, this.max); } // @ts-ignore // eslint-disable-next-line no-underscore-dangle snap._raf.play(); } /** If the start has been reached */ get isStart() { if (this.snap.props.loop) { return false; } return Math.floor(this.target) <= Math.floor(this.min); } /** If the end has been reached */ get isEnd() { if (this.snap.props.loop) { return false; } return Math.floor(this.target) >= Math.floor(this.max); } /** Check if the active slide is larger than the container and is being scrolled */ get isSlideScrolling() { const { snap } = this; const { domSize } = snap; return snap.scrollableSlides.some(({ size, coord }) => inRange(coord, domSize - size, 0)); } } //# sourceMappingURL=index.js.map