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.

180 lines (138 loc) 4.09 kB
import { clamp, inRange, lerp, scoped } from '@/utils/math'; import { Snap } from '..'; import { toPixels } from '@/utils'; export class SnapTrack { constructor(protected snap: Snap) {} /** The current track value */ protected _current = 0; /** The target track value */ protected _target = 0; /** Gets the current track value. */ get current() { return this._current; } /** Sets the current track value */ set current(value: number) { this._current = value; } /** Gets the target track value. */ get target() { return this._target; } /** Sets the target track value */ set target(value: number) { this._target = value; } /** Set a value to current & target value instantly */ public set(value: number) { this.current = value; this.target = value; } /** If can loop */ get canLoop() { return this.snap.props.loop && this.snap.slides.length > 1; } /** Interpolate the current track value */ public lerp(factor: number) { // todo: move to renderer? 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); } 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 */ public iterateTarget(delta: number) { 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 */ public 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() { return Math.round(this.target) <= this.min; } /** If the end has been reached */ get isEnd() { return Math.round(this.target) >= 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.slides.some( ({ size, coord }) => size > domSize && inRange(coord, domSize - size, 0), ); } }