Skip to content

Commit

Permalink
♻️ : use an imperative handle to control the video instead
Browse files Browse the repository at this point in the history
  • Loading branch information
hsunpei committed Oct 10, 2024
1 parent 2a056af commit 206b651
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 26 deletions.
8 changes: 7 additions & 1 deletion apps/docs/components/video/StickyVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ export const StickyVideo = ({ src }: StickyVideoProps) => {
}
>
<div ref={videoContainerRef} className="absolute left-0 right-0 top-14 h-screen">
<Video width={width} height={height} src={src} ratio={scrolledRatio} />
<Video
className="h-full w-full object-cover"
width={width}
height={height}
src={src}
ratio={Math.round(scrolledRatio * 100) / 100}
/>
</div>
</StickyContainer>
);
Expand Down
72 changes: 47 additions & 25 deletions packages/video/src/components/Video.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useImperativeHandle, useRef } from 'react';

export interface VideoProps {
src: string;
Expand All @@ -12,39 +12,61 @@ export interface VideoProps {
// - thumbnail
// - preload
// - use imperative handle to play instead of changing through props
// - loadedmetadata

export const Video = React.memo(({ src, width, height, ratio = 0, className }: VideoProps) => {
const videoRef = useRef<HTMLVideoElement>(null);
export const Video = ({ src, width, height, ratio = 0, className }: VideoProps) => {
const videoRef = useRef<VideoRef>(null);

useEffect(() => {
const videoElement = videoRef.current;
if (videoElement) {
const updateCurrentTime = () => {
const duration = videoElement.duration;
if (!isNaN(duration)) {
videoElement.currentTime = ratio * duration;
}
};

// update currentTime when the video metadata is loaded
videoElement.addEventListener('loadedmetadata', updateCurrentTime);

// update currentTime when the percentage prop changes
updateCurrentTime();

return () => {
videoElement.removeEventListener('loadedmetadata', updateCurrentTime);
};
// update currentTime when the percentage prop changes
if (!videoRef.current) {
return;
}

videoRef.current.setRatio(ratio);
}, [ratio]);

return (
<video
<VideoWithImperativeHandle
ref={videoRef}
width={width}
height={height}
src={src}
className={`h-full w-full object-cover ${className}`}
></video>
className={className}
></VideoWithImperativeHandle>
);
});
};

export interface VideoRef {
setRatio: (ratio: number) => void;
}

export const VideoWithImperativeHandle = React.memo(
React.forwardRef<VideoRef, VideoProps>(({ src, width, height, className }, ref) => {
const videoRef = useRef<HTMLVideoElement>(null);

useImperativeHandle(
ref,
() => {
return {
setRatio: (ratio: number) => {
const videoElement = videoRef.current;
if (!videoElement) {
return;
}

const duration = videoElement.duration;
if (!isNaN(duration)) {
videoElement.currentTime = ratio * duration;
}
},
};
},
[]
);

return (
<video ref={videoRef} width={width} height={height} src={src} className={className}></video>
);
})
);

0 comments on commit 206b651

Please sign in to comment.