From f8421b675ef892296f8436c9848285128ae6b41f Mon Sep 17 00:00:00 2001 From: Sudev Kiyada Date: Mon, 2 Jun 2025 23:40:09 +0530 Subject: [PATCH] fixes types --- src/components/ScrollyVideo/ScrollyVideo.mdx | 26 +++++++++ .../ScrollyVideo/ScrollyVideo.stories.svelte | 55 +++++++++++++------ .../ScrollyVideo/ScrollyVideo.svelte | 10 ++-- .../ScrollyVideo/js/ScrollyVideo.d.ts | 28 +++++----- .../ScrollyVideo/js/ScrollyVideo.js | 19 ++++++- .../ScrollyVideo/js/state.svelte.ts | 2 +- src/components/ScrollyVideo/js/utils.d.ts | 27 ++++++++- src/components/ScrollyVideo/js/utils.js | 8 +-- .../ScrollyVideo/js/videoDecoder.d.ts | 8 +-- .../ScrollyVideo/js/videoDecoder.js | 2 +- 10 files changed, 131 insertions(+), 54 deletions(-) diff --git a/src/components/ScrollyVideo/ScrollyVideo.mdx b/src/components/ScrollyVideo/ScrollyVideo.mdx index 344478f1..78006058 100644 --- a/src/components/ScrollyVideo/ScrollyVideo.mdx +++ b/src/components/ScrollyVideo/ScrollyVideo.mdx @@ -27,6 +27,8 @@ The `ScrollyVideo` component is built on top of the [ScrollyVideo.js](https://sc To use the `ScrollyVideo` component, simply import it and provide the video source. Set height prop to any valid CSS height value, such as `1200px`, `50vh`, or `500svh` to set the scrolling height. +[Demo](?path=/story/components-graphics-scrollyvideo--basic) + ```svelte + + {#if width < 600} import { ScrollyVideo } from '@reuters-graphics/graphics-components'; @@ -98,6 +108,8 @@ Autoplay can be enabled by setting the `autoplay` prop to `true`. This will star The `ScrollyVideo` component can also be used inside the `ScrollerBase` component to create a scroller-based layout. This allows for more complex interactions and layouts, such as combining ai2svelte components with scrolly video content. +[Demo](?path=/story/components-graphics-scrollyvideo--inside-scroller-base) + ```svelte @@ -144,28 +154,37 @@ - - {#if width < 600} - - {:else if width < 1200} - - {:else} - - {/if} + + {#snippet children(args)} + {#key args} + {#if width < 600} + + {:else if width < 1200} + + {:else} + + {/if} + {/key} + {/snippet} - + {#snippet children(args)} + {#key args} + + {/key} + {/snippet} - + {#snippet children(args)} + {#key args} + + {/key} + {/snippet} diff --git a/src/components/ScrollyVideo/ScrollyVideo.svelte b/src/components/ScrollyVideo/ScrollyVideo.svelte index 79ca337b..dccdb827 100644 --- a/src/components/ScrollyVideo/ScrollyVideo.svelte +++ b/src/components/ScrollyVideo/ScrollyVideo.svelte @@ -65,7 +65,7 @@ }: Props = $props(); // variable to hold the DOM element - let scrollyVideoContainer = $state(); + let scrollyVideoContainer = $state(undefined); // Store the props so we know when things change let lastPropsString = ''; @@ -77,6 +77,8 @@ if (scrollyVideo && scrollyVideo.destroy) scrollyVideo.destroy(); scrollyVideo = new ScrollyVideo({ scrollyVideoContainer, + onReady, + onChange, ...restProps, }); @@ -102,9 +104,7 @@ }); let heightChange = $derived.by(() => { - return scrollyVideoState.isAutoPlaying ? - `calc(${height} * ${1 - scrollyVideoState.framesData.currentFrame / scrollyVideoState.framesData.totalFrames})` - : `calc(${height} * ${1 - scrollyVideoState.autoplayProgress})`; + return `calc(${height} * ${1 - scrollyVideoState.autoplayProgress})`; }); @@ -129,7 +129,7 @@
{#if showDebugInfo}

- {@html JSON.stringify(flattenObject([scrollyVideoState])) + {@html JSON.stringify(flattenObject(scrollyVideoState)) .replace(/[{}"]/g, '') .split(',') .join('
')} diff --git a/src/components/ScrollyVideo/js/ScrollyVideo.d.ts b/src/components/ScrollyVideo/js/ScrollyVideo.d.ts index 9692bfee..78ceb82a 100644 --- a/src/components/ScrollyVideo/js/ScrollyVideo.d.ts +++ b/src/components/ScrollyVideo/js/ScrollyVideo.d.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-empty-object-type */ -/* eslint-disable @typescript-eslint/no-explicit-any */ + export default ScrollyVideo; /** * ____ _ _ __ ___ _ @@ -29,8 +29,8 @@ declare class ScrollyVideo { debug, autoplay, }: { - src?: any; - scrollyVideoContainer: any; + src?: string; + scrollyVideoContainer: string | HTMLDivElement | undefined; objectFit?: string; sticky?: boolean; full?: boolean; @@ -45,7 +45,7 @@ declare class ScrollyVideo { autoplay?: boolean; }); container: Element; - src: any; + src: string; transitionSpeed: number; frameThreshold: number; useWebCodecs: boolean; @@ -63,11 +63,11 @@ declare class ScrollyVideo { targetTime: number; canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; - frames: any[]; + frames: ImageBitmap[] | null; frameRate: number; currentFrameNumber: number; - updateScrollPercentage: (jump: any) => void; - targetScrollPosition: any; + updateScrollPercentage: (jump: boolean) => void; + targetScrollPosition: number | null; resize: () => void; /** * Sets the currentTime of the video as a specified percentage of its total duration. @@ -78,13 +78,13 @@ declare class ScrollyVideo { * - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8. * - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition. */ - setVideoPercentage(percentage: any, options?: {}): void; + setVideoPercentage(percentage: number, options?: {}): void; /** * Sets the style of the video or canvas to "cover" it's container * * @param el */ - setCoverStyle(el: any): void; + setCoverStyle(el: string): void; /** * Uses webCodecs to decode the video into frames */ @@ -94,7 +94,7 @@ declare class ScrollyVideo { * * @param frameNum */ - paintCanvasFrame(frameNum: any): void; + paintCanvasFrame(frameNum: number): void; /** * Transitions the video or the canvas to the proper frame. * @@ -108,9 +108,9 @@ declare class ScrollyVideo { transitionSpeed, easing, }: { - jump: any; + jump: boolean; transitionSpeed?: number; - easing?: any; + easing?: (progress: number) => number; }): void; transitioningRaf: number; /** @@ -122,13 +122,13 @@ declare class ScrollyVideo { * - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8. * - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition. */ - setTargetTimePercent(percentage: any, options?: {}): void; + setTargetTimePercent(percentage: number, options?: {}): void; /** * Simulate trackScroll programmatically (scrolls on page by percentage of video) * * @param percentage */ - setScrollPercent(percentage: any): void; + setScrollPercent(percentage: number): void; /** * Call to destroy this ScrollyVideo object */ diff --git a/src/components/ScrollyVideo/js/ScrollyVideo.js b/src/components/ScrollyVideo/js/ScrollyVideo.js index 044a0c71..0f74727a 100644 --- a/src/components/ScrollyVideo/js/ScrollyVideo.js +++ b/src/components/ScrollyVideo/js/ScrollyVideo.js @@ -75,6 +75,17 @@ class ScrollyVideo { this.video.pause(); this.video.load(); + this.video.addEventListener( + 'canplaythrough', + () => { + this.onReady(); + if (this.autoplay && !this.useWebCodecs) { + this.autoplayScroll(); + } + }, + { once: true } + ); + // Start the video percentage at 0 this.videoPercentage = 0; @@ -190,7 +201,6 @@ class ScrollyVideo { this.updateScrollPercentage(true); this.totalTime = this.video.duration; this.setCoverStyle(this.canvas || this.video); - if (this.autoplay) this.autoplayScroll(); }, { once: true } ); @@ -201,7 +211,6 @@ class ScrollyVideo { this.setTargetTimePercent(0, { jump: true }); this.totalTime = this.video.duration; this.setCoverStyle(this.canvas || this.video); - if (this.autoplay) this.autoplayScroll(); }, { once: true } ); @@ -372,6 +381,8 @@ class ScrollyVideo { // Paint our first frame this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate)); this.onReady(); + + if (this.autoplay) this.autoplayScroll(); } /** @@ -456,6 +467,10 @@ class ScrollyVideo { this.currentTime >= this.targetTime : this.currentTime <= this.targetTime; + if (scrollyVideoState.isAutoPlaying) { + scrollyVideoState.autoplayProgress = this.currentTime / this.totalTime; + } + // If we are already close enough to our target, pause the video and return. // This is the base case of the recursive function if ( diff --git a/src/components/ScrollyVideo/js/state.svelte.ts b/src/components/ScrollyVideo/js/state.svelte.ts index cbe89594..1ace7509 100644 --- a/src/components/ScrollyVideo/js/state.svelte.ts +++ b/src/components/ScrollyVideo/js/state.svelte.ts @@ -12,7 +12,7 @@ type FramesData = { totalFrames: number; }; -type ScrollyVideoState = { +export type ScrollyVideoState = { generalData: GeneralData; usingWebCodecs: boolean; framesData: FramesData; diff --git a/src/components/ScrollyVideo/js/utils.d.ts b/src/components/ScrollyVideo/js/utils.d.ts index ce0992c9..1a38d614 100644 --- a/src/components/ScrollyVideo/js/utils.d.ts +++ b/src/components/ScrollyVideo/js/utils.d.ts @@ -1,6 +1,25 @@ -export function debounce(func: any, delay?: number): (...args: any[]) => void; +import type { ScrollyVideoState } from './types'; + +type FlattenedScrollyVideoState = { + src: string; + videoPercentage: number; + frameRate: number; + currentTime: number; + totalTime: number; + usingWebCodecs: boolean; + codec: string; + currentFrame: number; + totalFrames: number; + isAutoPlaying: boolean; + autoplayProgress: number; +}; + +export function debounce unknown>( + func: T, + delay?: number +): (...args: Parameters) => void; export function isScrollPositionAtTarget( - targetScrollPosition: any, + targetScrollPosition: number | null, threshold?: number ): boolean; function constrain(n: number, low: number, high: number): number; @@ -12,4 +31,6 @@ export function map( stop2: number, withinBounds?: boolean ): number; -export function flattenObject(obj: any): any; +export function flattenObject( + obj: ScrollyVideoState +): FlattenedScrollyVideoState; diff --git a/src/components/ScrollyVideo/js/utils.js b/src/components/ScrollyVideo/js/utils.js index 67a75af4..409db621 100644 --- a/src/components/ScrollyVideo/js/utils.js +++ b/src/components/ScrollyVideo/js/utils.js @@ -1,15 +1,13 @@ export function debounce(func, delay = 0) { let timeoutId; - return function (...args) { - const context = this; - + return (...args) => { // Clear the previous timeout if it exists clearTimeout(timeoutId); // Set a new timeout to call the function later timeoutId = setTimeout(() => { - func.apply(context, args); + func.apply(this, args); }, delay); }; } @@ -21,8 +19,6 @@ export const isScrollPositionAtTarget = ( const currentScrollPosition = window.pageYOffset; const difference = Math.abs(currentScrollPosition - targetScrollPosition); - console.log(targetScrollPosition, currentScrollPosition, difference); - return difference < threshold; }; diff --git a/src/components/ScrollyVideo/js/videoDecoder.d.ts b/src/components/ScrollyVideo/js/videoDecoder.d.ts index 7a51df4f..c6f05a14 100644 --- a/src/components/ScrollyVideo/js/videoDecoder.d.ts +++ b/src/components/ScrollyVideo/js/videoDecoder.d.ts @@ -1,6 +1,6 @@ declare function _default( - src: any, - emitFrame: any, - debug: any -): Promise | Promise | any; + src: string, + emitFrame: (frame: ImageBitmap) => void, + debug: boolean +): Promise | Promise; export default _default; diff --git a/src/components/ScrollyVideo/js/videoDecoder.js b/src/components/ScrollyVideo/js/videoDecoder.js index cac62ea6..b0ac043d 100644 --- a/src/components/ScrollyVideo/js/videoDecoder.js +++ b/src/components/ScrollyVideo/js/videoDecoder.js @@ -152,7 +152,7 @@ const decodeVideo = ( } else reject(new Error('URL provided is not a valid mp4 video file.')); }; - mp4boxfile.onSamples = (track_id, ref, samples) => { + mp4boxfile.onSamples = (_track_id, _ref, samples) => { for (let i = 0; i < samples.length; i += 1) { const sample = samples[i]; const type = sample.is_sync ? 'key' : 'delta';