diff --git a/README.md b/README.md index dc9edde..bfcd5fe 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Add html code to your html component: | trackScroll | Whether this object should automatically respond to scroll | Boolean | true | | useWebCodecs | Whether the library should use the webcodecs method, see below | Boolean | true | | videoPercentage | Manually specify the position of the video between 0..1, only used for react, vue, and svelte components | Number | | +| onReady | The callback when it's ready to scroll | VoidFunction | | | debug | Whether to log debug information | Boolean | false | diff --git a/src/ScrollyVideo.js b/src/ScrollyVideo.js index 797f5ac..4d6fd77 100644 --- a/src/ScrollyVideo.js +++ b/src/ScrollyVideo.js @@ -23,6 +23,7 @@ class ScrollyVideo { transitionSpeed = 8, // How fast the video transitions between points frameThreshold = 0.1, // When to stop the video animation, in seconds useWebCodecs = true, // Whether to try using the webcodecs approach + onReady = () => {}, // A callback that invokes on video decode debug = false, // Whether to print debug stats to the console }) { // Make sure that we have a DOM @@ -62,8 +63,8 @@ class ScrollyVideo { this.useWebCodecs = useWebCodecs; this.cover = cover; this.sticky = sticky; - this.full = full; this.trackScroll = trackScroll; + this.onReady = onReady; this.debug = debug; // Create the initial video object. Even if we are going to use webcodecs, @@ -212,51 +213,66 @@ class ScrollyVideo { /** * Uses webCodecs to decode the video into frames */ - decodeVideo() { - if (this.useWebCodecs && this.src) { - videoDecoder( + async decodeVideo() { + if (!this.useWebCodecs) { + if (this.debug) + console.warn('Cannot perform video decode: `useWebCodes` disabled'); + + return; + } + + if (!this.src) { + if (this.debug) + console.warn('Cannot perform video decode: no `src` found'); + + return; + } + + try { + await videoDecoder( this.src, (frame) => { this.frames.push(frame); }, this.debug, - ) - .catch(() => { - if (this.debug) - console.error('Error encountered while decoding video'); - // Remove all decoded frames if a failure happens during decoding - this.frames = []; - - // Force a video reload when videoDecoder fails - this.video.load(); - }) - .then(() => { - // If no frames, something went wrong - if (this.frames.length === 0) { - if (this.debug) - console.error('No frames were received from webCodecs'); - return; - } - - // Calculate the frameRate based on number of frames and the duration - this.frameRate = this.frames.length / this.video.duration; - if (this.debug) - console.info('Received', this.frames.length, 'frames'); - - // Remove the video and add the canvas - // eslint-disable-next-line no-undef - this.canvas = document.createElement('canvas'); - this.context = this.canvas.getContext('2d'); + ); + } catch (error) { + if (this.debug) + console.error('Error encountered while decoding video', error); - // Hide the video and add the canvas to the container - this.video.style.display = 'none'; - this.container.appendChild(this.canvas); - if (this.cover) this.setCoverStyle(this.canvas); + // Remove all decoded frames if a failure happens during decoding + this.frames = []; - // Paint our first frame - this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate)); - }); + // Force a video reload when videoDecoder fails + this.video.load(); + } + + // If no frames, something went wrong + if (this.frames.length === 0) { + if (this.debug) console.error('No frames were received from webCodecs'); + + this.onReady(); + return; } + + // Calculate the frameRate based on number of frames and the duration + this.frameRate = this.frames.length / this.video.duration; + if (this.debug) console.info('Received', this.frames.length, 'frames'); + + // Remove the video and add the canvas + // eslint-disable-next-line no-undef + this.canvas = document.createElement('canvas'); + this.context = this.canvas.getContext('2d'); + + // Hide the video and add the canvas to the container + this.video.style.display = 'none'; + this.container.appendChild(this.canvas); + if (this.cover) this.setCoverStyle(this.canvas); + + // Paint our first frame + this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate)); + + this.onReady(); } /** diff --git a/src/ScrollyVideo.jsx b/src/ScrollyVideo.jsx index 91a8414..3542570 100644 --- a/src/ScrollyVideo.jsx +++ b/src/ScrollyVideo.jsx @@ -19,6 +19,7 @@ const ScrollyVideoComponent = forwardRef(function ScrollyVideoComponent( useWebCodecs, videoPercentage, debug, + onReady, }, ref, ) { @@ -29,6 +30,9 @@ const ScrollyVideoComponent = forwardRef(function ScrollyVideoComponent( const videoPercentageRef = useRef(videoPercentage); videoPercentageRef.current = videoPercentage; + const onReadyRef = useRef(onReady); + onReadyRef.current = onReady; + // effect for destroy and recreate on props change (except video percentage) useEffect(() => { if (!containerElement.current) return; @@ -50,6 +54,7 @@ const ScrollyVideoComponent = forwardRef(function ScrollyVideoComponent( useWebCodecs, debug, videoPercentage: videoPercentageRef.current, + onReady: onReadyRef.current, }); setInstance(scrollyVideo);