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.

223 lines 8.88 kB
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; }; import { Module } from '../../base'; import { initVevet } from '../../global/initVevet'; import { noopIfDestroyed } from '../../internal/noopIfDestroyed'; import { addEventListener, clampScope } from '../../utils'; import { MUTABLE_PROPS, STATIC_PROPS } from './props'; export * from './types'; /** * `ScrollProgress` is a component that tracks the scroll progress of a specified section element. * * This component can be used for creating scroll-based animations such as parallax effects. * * [Documentation](https://vevetjs.com/docs/ScrollProgress) * * @group Components */ export class ScrollProgress extends Module { /** Retrieves the default static properties. */ _getStatic() { return Object.assign(Object.assign({}, super._getStatic()), STATIC_PROPS); } /** Retrieves the default mutable properties. */ _getMutable() { return Object.assign(Object.assign({}, super._getMutable()), MUTABLE_PROPS); } constructor(props, onCallbacks) { super(props, onCallbacks); /** Indicates whether the section is currently visible within the viewport or root element. */ this._isVisible = false; /** The bounds of the root element used for scroll calculations. */ this._rootBounds = { top: 0, left: 0, width: 1, height: 1, }; /** The bounds of the section element relative to the root element. */ this._sectionBounds = { top: 0, left: 0, width: 1, height: 1, }; this._isVisible = !this.props.optimized; this._setup(); } /** * Returns the section element being tracked for scroll progress. */ get section() { return this.props.section; } /** Indicates whether the section is currently visible within the viewport or root element. */ get isVisible() { return this._isVisible; } /** The bounds of the root element used for scroll calculations. */ get rootBounds() { return this._rootBounds; } /** The bounds of the section element relative to the root element. */ get sectionBounds() { return this._sectionBounds; } /** Sets up events */ _setup() { this._setupObserver(); this._setupScroll(); } /** * Sets up an `IntersectionObserver` to track the visibility of the section. */ _setupObserver() { if (!this.props.optimized) { // Initial Update this.update(true); return; } const { section } = this.props; // Initial Update const bounding = section.getBoundingClientRect(); this._isVisible = bounding.top < window.innerHeight || bounding.bottom > 0; this.update(true); // Observer Update const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.target === section) { const isNowVisible = entry.isIntersecting; if (isNowVisible === this._isVisible) { return; } this._isVisible = entry.isIntersecting; this.update(); } }); }); observer.observe(section); this.onDestroy(() => observer.disconnect()); } /** * Sets up a scroll event listener to track and update progress. */ _setupScroll() { const container = this.props.root || window; const listener = addEventListener(container, 'scroll', () => this.update(), { passive: false }); this.onDestroy(listener); } /** Updates the section and root bounds, and emits an update callback. */ update(isForce = false) { if (!this.isVisible && !isForce) { return; } const { section, props } = this; const container = props.root; const core = initVevet(); const sectionBounding = section.getBoundingClientRect(); const viewportBounds = { top: 0, left: 0, width: core.width, height: props.useSvh ? core.sHeight : core.height, }; this._rootBounds = container ? container.getBoundingClientRect() : viewportBounds; this._sectionBounds = { top: sectionBounding.top - this._rootBounds.top, left: sectionBounding.left - this._rootBounds.left, width: sectionBounding.width, height: sectionBounding.height, }; this.callbacks.emit('update', undefined); } /** * Calculates the section scroll progress relative to the root element. * * The function takes top or left corner of the section as the reference point. * * @param topThreshold - Top threshold of the section position. * @param rightThreshold - Right threshold of the section position. * @param bottomThreshold - Bottom threshold of the section position. * @param leftThreshold - Left threshold of the section position. * @returns The scroll progress along the x and y axes. * * @example * * const progress = getProgress(0, vevet.width, vevet.height / 2, 0) * * // `progress.y` is `0` when the top corner of the section is at the beginning of the viewport or root element * // `progress.y` is `1` when the top corner of the section is at the center of the viewport or root element */ getProgress(topThreshold, rightThreshold, bottomThreshold, leftThreshold) { const y = clampScope(this._sectionBounds.top, [ topThreshold, bottomThreshold, ]); const x = clampScope(this._sectionBounds.left, [ leftThreshold, rightThreshold, ]); return { x: Number.isNaN(x) ? 0 : x, y: Number.isNaN(y) ? 0 : y, }; } /** Calculates the progress of the section entering the root element. */ get inProgress() { const { rootBounds, sectionBounds } = this; const top = this.rootBounds.height; const right = sectionBounds.width > rootBounds.width ? 0 : rootBounds.width - sectionBounds.width; const bottom = sectionBounds.height > rootBounds.height ? 0 : rootBounds.height - sectionBounds.height; const left = this.rootBounds.width; return this.getProgress(top, right, bottom, left); } /** Calculates the progress of the section leaving the root element. */ get outProgress() { const { rootBounds, sectionBounds } = this; const top = Math.min(rootBounds.height - sectionBounds.height, 0); const right = -sectionBounds.width; const bottom = -sectionBounds.height; const left = Math.min(rootBounds.width - sectionBounds.width, 0); return this.getProgress(top, right, bottom, left); } /** Calculates the progress of the section's movement within the root element. */ get moveProgress() { const { rootBounds, sectionBounds } = this; const top = sectionBounds.height > rootBounds.height ? 0 : rootBounds.height - sectionBounds.height; const right = sectionBounds.width > rootBounds.width ? -(sectionBounds.width - rootBounds.width) : 0; const bottom = sectionBounds.height > rootBounds.height ? -(sectionBounds.height - rootBounds.height) : 0; const left = sectionBounds.width > rootBounds.width ? 0 : rootBounds.width - sectionBounds.width; return this.getProgress(top, right, bottom, left); } /** Calculates the global scroll progress of the section relative to the root element. */ get progress() { const { sectionBounds, rootBounds } = this; const top = rootBounds.height; const right = -sectionBounds.width; const bottom = -sectionBounds.height; const left = rootBounds.width; return this.getProgress(top, right, bottom, left); } } __decorate([ noopIfDestroyed ], ScrollProgress.prototype, "update", null); //# sourceMappingURL=index.js.map