Skip to content

Commit

Permalink
feat: ready callback (#98)
Browse files Browse the repository at this point in the history
* feat: add onReady callback

* docs: update readme
  • Loading branch information
tarsinzer committed May 3, 2024
1 parent a3cb753 commit cdc5ed3
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 38 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |


Expand Down
92 changes: 54 additions & 38 deletions src/ScrollyVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/ScrollyVideo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const ScrollyVideoComponent = forwardRef(function ScrollyVideoComponent(
useWebCodecs,
videoPercentage,
debug,
onReady,
},
ref,
) {
Expand All @@ -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;
Expand All @@ -50,6 +54,7 @@ const ScrollyVideoComponent = forwardRef(function ScrollyVideoComponent(
useWebCodecs,
debug,
videoPercentage: videoPercentageRef.current,
onReady: onReadyRef.current,
});

setInstance(scrollyVideo);
Expand Down

0 comments on commit cdc5ed3

Please sign in to comment.