adds jsdoc style comments

This commit is contained in:
Sudev Kiyada 2025-06-03 19:45:39 +05:30
parent e897778dcc
commit 73285cd91b
Failed to extract signature
4 changed files with 266 additions and 19 deletions

View file

@ -5,7 +5,32 @@
import type { Snippet } from 'svelte';
import { setContext } from 'svelte';
// Define the props interface
/**
* Props for the ScrollyVideo Svelte component.
* @typedef {Object} Props
* @property {string} [class] - CSS class for scrolly container.
* @property {string} [id] - ID of the scrolly container.
* @property {ScrollyVideo} [scrollyVideo] - Bindable instance of ScrollyVideo.
* @property {string} [src] - Video source URL.
* @property {number} [videoPercentage] - Bindable percentage value to control video playback. Ranges from 0 to 1.
* @property {number} [transitionSpeed] - Sets the maximum playbackRate for this video.
* @property {number} [frameThreshold] - When to stop the video animation, in seconds.
* @property {string} [objectFit] - How the video should be resized to fit its container.
* @property {boolean} [sticky] - Whether the video should have position: sticky.
* @property {boolean} [full] - Whether the video should take up the entire viewport.
* @property {boolean} [trackScroll] - Whether this object should automatically respond to scroll. Set this to false while manually controlling `videoPercentage` prop.
* @property {boolean} [lockScroll] - Whether it ignores human scroll while it runs setVideoPercentage with enabled trackScroll.
* @property {boolean} [useWebCodecs] - Whether the library should use the webcodecs method. For more info, visit https://scrollyvideo.js.org/
* @property {() => void} [onReady] - The callback when it's ready to scroll.
* @property {() => void} [onChange] - The callback for video percentage change.
* @property {boolean} [debug] - Whether to log debug information. Internal library logs.
* @property {boolean} [showDebugInfo] - Shows debug information on page.
* @property {string} [height] - Height of the video container. Set it to 100svh when using inside `ScrollerBase`.
* @property {boolean} [autoplay] - Whether the video should autoplay.
* @property {boolean} [embedded] - Variable to control component rendering on embed page.
* @property {string} [embeddedSrc] - Source for the embedded video. If not provided, defaults to `src`.
* @property {Snippet} [children] - Children render function.
*/
interface Props {
/** CSS class for scrolly container */
class?: string;
@ -53,6 +78,10 @@
children?: Snippet;
}
/**
* Main logic for ScrollyVideo Svelte component.
* Handles instantiation, prop changes, and cleanup.
*/
let {
scrollyVideo = $bindable(),
videoPercentage,
@ -69,6 +98,10 @@
}: Props = $props();
// variable to hold the DOM element
/**
* Reference to the scrolly video container DOM element.
* @type {HTMLDivElement | undefined}
*/
let scrollyVideoContainer = $state<HTMLDivElement | undefined>(undefined);
// Store the props so we know when things change
@ -106,12 +139,17 @@
}
});
// Cleanup the component on destroy
/**
* Cleanup the component on destroy.
*/
onDestroy(() => {
if (scrollyVideo && scrollyVideo.destroy) scrollyVideo.destroy();
});
// heightChange drives the height of the component when autoplay is set to true
/**
* heightChange drives the height of the component when autoplay is set to true.
* @type {string}
*/
let heightChange = $derived.by(() => {
if (scrollyVideo) {
return `calc(${height} * ${1 - scrollyVideo?.componentState.autoplayProgress})`;

View file

@ -27,39 +27,165 @@ interface TransitionOptions {
autoplay?: boolean;
}
/**
* ScrollyVideo class for scroll-driven or programmatic video playback with Svelte integration.
*/
class ScrollyVideo {
/**
* The container element for the video or canvas.
* @type {HTMLElement | null}
*/
container: HTMLElement | null;
/**
* The original container argument (element or string ID).
* @type {Element | string | undefined}
*/
scrollyVideoContainer: Element | string | undefined;
/**
* Video source URL.
* @type {string}
*/
src: string;
/**
* Speed of transitions.
* @type {number}
*/
transitionSpeed: number;
/**
* Threshold for frame transitions.
* @type {number}
*/
frameThreshold: number;
/**
* Whether to use WebCodecs for decoding.
* @type {boolean}
*/
useWebCodecs: boolean;
/**
* CSS object-fit property for video/canvas.
* @type {string}
*/
objectFit: string;
/**
* Whether to use sticky positioning.
* @type {boolean}
*/
sticky: boolean;
/**
* Whether to track scroll position.
* @type {boolean}
*/
trackScroll: boolean;
/**
* Callback when ready.
* @type {() => void}
*/
onReady: () => void;
/**
* Callback on scroll percentage change.
* @type {(percentage?: number) => void}
*/
onChange: (percentage?: number) => void;
/**
* Enable debug logging.
* @type {boolean}
*/
debug: boolean;
/**
* Enable autoplay.
* @type {boolean}
*/
autoplay: boolean;
/**
* The HTML video element.
* @type {HTMLVideoElement | undefined}
*/
video: HTMLVideoElement | undefined;
/**
* Current scroll/video percentage (0-1).
* @type {number}
*/
videoPercentage: number;
/**
* True if browser is Safari.
* @type {boolean}
*/
isSafari: boolean;
/**
* Current video time in seconds.
* @type {number}
*/
currentTime: number;
/**
* Target video time in seconds.
* @type {number}
*/
targetTime: number;
/**
* Canvas for rendering frames (if using WebCodecs).
* @type {HTMLCanvasElement | null}
*/
canvas: HTMLCanvasElement | null;
/**
* 2D context for the canvas.
* @type {CanvasRenderingContext2D | null}
*/
context: CanvasRenderingContext2D | null;
/**
* Decoded video frames (if using WebCodecs).
* @type {ImageBitmap[] | null}
*/
frames: ImageBitmap[] | null;
/**
* Video frame rate.
* @type {number}
*/
frameRate: number;
/**
* Target scroll position in pixels, if set.
* @type {number | null}
*/
targetScrollPosition: number | null = null;
/**
* Current frame index (if using WebCodecs).
* @type {number}
*/
currentFrame: number;
usingWebCodecs: boolean; // Whether we are using webCodecs
totalTime: number; // The total time of the video, used for calculating percentage
/**
* True if using WebCodecs for decoding.
* @type {boolean}
*/
usingWebCodecs: boolean;
/**
* Total video duration in seconds.
* @type {number}
*/
totalTime: number;
/**
* RequestAnimationFrame ID for transitions.
* @type {number | null}
*/
transitioningRaf: number | null;
componentState: ScrollyVideoState; // Placeholder for component state, if needed
/**
* State object for component-level state.
* @type {ScrollyVideoState}
*/
componentState: ScrollyVideoState;
/**
* Function to update scroll percentage (set in constructor).
* @type {((jump: boolean) => void) | undefined}
*/
updateScrollPercentage: ((jump: boolean) => void) | undefined;
/**
* Function to handle resize events (set in constructor).
* @type {(() => void) | undefined}
*/
resize: (() => void) | undefined;
/**
* Creates a new ScrollyVideo instance.
* @param {ScrollyVideoArgs} args - The arguments for initialization.
*/
constructor({
src = 'https://scrollyvideo.js.org/goldengate.mp4',
scrollyVideoContainer,
@ -356,9 +482,8 @@ class ScrollyVideo {
}
/**
* Sets the style of the video or canvas to "cover" it's container
*
* @param el
* Sets the style of the video or canvas to "cover" its container.
* @param {HTMLElement | HTMLCanvasElement | undefined} el - The element to style.
*/
setCoverStyle(el: HTMLElement | HTMLCanvasElement | undefined): void {
if (!el) {
@ -418,9 +543,10 @@ class ScrollyVideo {
}
/**
* Uses webCodecs to decode the video into frames
* Uses webCodecs to decode the video into frames.
* @returns {Promise<void>} Resolves when decoding is complete.
*/
async decodeVideo() {
async decodeVideo(): Promise<void> {
if (!this.useWebCodecs) {
if (this.debug)
console.warn('Cannot perform video decode: `useWebCodes` disabled');
@ -496,9 +622,8 @@ class ScrollyVideo {
}
/**
* Paints the frame of to the canvas
*
* @param frameNum
* Paints the frame to the canvas.
* @param {number} frameNum - The frame index to paint.
*/
paintCanvasFrame(frameNum: number): void {
if (!this.frames) {
@ -751,9 +876,8 @@ class ScrollyVideo {
}
/**
* Simulate trackScroll programmatically (scrolls on page by percentage of video)
*
* @param percentage
* Simulate trackScroll programmatically (scrolls on page by percentage of video).
* @param {number} percentage - The percentage of the video to scroll to.
*/
setScrollPercent(percentage: number) {
if (!this.trackScroll) {
@ -785,7 +909,7 @@ class ScrollyVideo {
}
/**
* Call to destroy this ScrollyVideo object
* Call to destroy this ScrollyVideo object.
*/
destroy() {
if (this.debug) console.info('Destroying ScrollyVideo');
@ -801,6 +925,9 @@ class ScrollyVideo {
if (this.container) this.container.innerHTML = '';
}
/**
* Autoplay the video by scrolling to the end.
*/
autoplayScroll() {
this.setVideoPercentage(1, {
jump: false,
@ -811,6 +938,9 @@ class ScrollyVideo {
this.componentState.isAutoPlaying = true;
}
/**
* Updates debug information in the component state.
*/
updateDebugInfo() {
this.componentState.generalData.src = this.src;
this.componentState.generalData.videoPercentage = parseFloat(

View file

@ -1,3 +1,12 @@
/**
* General video data for ScrollyVideo state.
* @typedef {Object} GeneralData
* @property {string} src - Video source URL.
* @property {number} videoPercentage - Current video percentage (0-1).
* @property {number} frameRate - Video frame rate.
* @property {number} currentTime - Current video time in seconds.
* @property {number} totalTime - Total video duration in seconds.
*/
type GeneralData = {
src: string;
videoPercentage: number;
@ -6,12 +15,28 @@ type GeneralData = {
totalTime: number;
};
/**
* Frame-level data for ScrollyVideo state.
* @typedef {Object} FramesData
* @property {string} codec - Video codec string.
* @property {number} currentFrame - Current frame index.
* @property {number} totalFrames - Total number of frames.
*/
type FramesData = {
codec: string;
currentFrame: number;
totalFrames: number;
};
/**
* State object for ScrollyVideo component.
* @typedef {Object} ScrollyVideoState
* @property {GeneralData} generalData - General video data.
* @property {boolean} usingWebCodecs - Whether WebCodecs is used.
* @property {FramesData} framesData - Frame-level data.
* @property {boolean} isAutoPlaying - Whether video is autoplaying.
* @property {number} autoplayProgress - Progress of autoplay (0-1).
*/
export type ScrollyVideoState = {
generalData: GeneralData;
usingWebCodecs: boolean;
@ -20,6 +45,10 @@ export type ScrollyVideoState = {
autoplayProgress: number;
};
/**
* Creates a new ScrollyVideoState object with default values.
* @returns {ScrollyVideoState} The initialized state object.
*/
export function createComponentState(): ScrollyVideoState {
const scrollyVideoState = $state<ScrollyVideoState>({
generalData: {

View file

@ -1,5 +1,20 @@
import type { ScrollyVideoState } from './state.svelte';
/**
* Flattened version of ScrollyVideoState for easier access to all properties.
* @typedef {Object} FlattenedScrollyVideoState
* @property {string} src - Video source URL.
* @property {number} videoPercentage - Current video percentage (0-1).
* @property {number} frameRate - Video frame rate.
* @property {number} currentTime - Current video time in seconds.
* @property {number} totalTime - Total video duration in seconds.
* @property {boolean} usingWebCodecs - Whether WebCodecs is used.
* @property {string} codec - Video codec string.
* @property {number} currentFrame - Current frame index.
* @property {number} totalFrames - Total number of frames.
* @property {boolean} isAutoPlaying - Whether video is autoplaying.
* @property {number} autoplayProgress - Progress of autoplay (0-1).
*/
type FlattenedScrollyVideoState = {
src: string;
videoPercentage: number;
@ -14,6 +29,13 @@ type FlattenedScrollyVideoState = {
autoplayProgress: number;
};
/**
* Returns a debounced version of the given function.
* @template T
* @param {T} func - The function to debounce.
* @param {number} [delay=0] - The debounce delay in milliseconds.
* @returns {(...args: Parameters<T>) => void} The debounced function.
*/
export function debounce<T extends (...args: unknown[]) => void>(
func: T,
delay = 0
@ -28,6 +50,12 @@ export function debounce<T extends (...args: unknown[]) => void>(
};
}
/**
* Checks if the current scroll position is at the target position within a threshold.
* @param {number} targetScrollPosition - The target scroll position in pixels.
* @param {number} [threshold=1] - The allowed threshold in pixels.
* @returns {boolean} True if the current scroll position is within the threshold of the target.
*/
export const isScrollPositionAtTarget = (
targetScrollPosition: number,
threshold: number = 1
@ -38,10 +66,27 @@ export const isScrollPositionAtTarget = (
return difference < threshold;
};
/**
* Constrains a number between a lower and upper bound.
* @param {number} n - The number to constrain.
* @param {number} low - The lower bound.
* @param {number} high - The upper bound.
* @returns {number} The constrained value.
*/
function constrain(n: number, low: number, high: number): number {
return Math.max(Math.min(n, high), low);
}
/**
* Maps a number from one range to another.
* @param {number} n - The number to map.
* @param {number} start1 - Lower bound of the value's current range.
* @param {number} stop1 - Upper bound of the value's current range.
* @param {number} start2 - Lower bound of the value's target range.
* @param {number} stop2 - Upper bound of the value's target range.
* @param {boolean} [withinBounds=true] - Whether to constrain the result within the target range.
* @returns {number} The mapped value.
*/
export function map(
n: number,
start1: number,
@ -61,6 +106,11 @@ export function map(
}
}
/**
* Flattens a ScrollyVideoState object into a single-level object for easier access.
* @param {ScrollyVideoState} obj - The state object to flatten.
* @returns {FlattenedScrollyVideoState} The flattened state object.
*/
export function flattenObject(
obj: ScrollyVideoState
): FlattenedScrollyVideoState {