adds jsdoc style comments
This commit is contained in:
parent
e897778dcc
commit
73285cd91b
4 changed files with 266 additions and 19 deletions
|
|
@ -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})`;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue