这篇“怎么使用React+TS+IntersectionObserver实现视频懒加载和自动播放功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用React+TS+IntersectionObserver实现视频懒加载和自动播放功能”文章吧。
IntersectionObserver
IntersectionObserver 是浏览器内置的 API,用于监听元素是否交叉或超出了视口(viewport)的变化。这个 API 提供了一种异步观察目标元素的机制,在元素进入或离开视口时触发回调函数。
以下是 IntersectionObserver API 的基本语法:
const observer = new IntersectionObserver(callback, options);
其中
callback
是回调函数,options
是配置选项,用于指定观察器的参数,如视口的大小、元素与视口的交叉比例等。创建 IntersectionObserver 对象后,我们就可以监听目标元素:
observer.observe(target); // target 是被观察的目标元素
组件卸载时,应取消监听:
observer.unobserve(target);
如果监听了多个元素,可以停止监听所有目标元素:
observer.disconnect();
上面的
disconnect()
方法用于停止所有目标元素的监听,并销毁 IntersectionObserver 对象。现在,你应该已经了解了 IntersectionObserver API。下面,我们将通过编写一个可扩展的组件来实现效果图演示的功能。
视频播放控制组件
首先,我们定义一个
VideoProps
接口,它包含了我们的 Video
组件的属性:interface VideoProps {
src: string;
width?: number;
height?: number;
className?: string;
}
接下来,我们定义
Video
组件,它接收 src
、width
、height
和 className
属性:const Video: React.FC<VideoProps> = ({
src,
width = 400,
height = 300,
className,
}) => {
// ...
};
在
Video
组件中,我们通过 useRef
钩子来创建一个 videoRef
引用。我们将通过该引用来判断视频状态、更新视频真实地址、控制视频的播放和暂停功能。const videoRef = useRef<HTMLVideoElement>(null);
然后,我们使用
useEffect
钩子来创建一个 IntersectionObserver
实例。我们要实现的功能主要就是通过这个实例来实现的。useEffect(() => {
const video = videoRef.current;
const options = {
rootMargin: "0px",
threshold: 0.5, // 指定交叉比例为 50% 时触发回调函数
};
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver(([entry]) => {
// ...
}, options);
// 监听 video 元素
if (video) {
observer.observe(video);
}
// 组件卸载时取消监听
return () => {
observer.unobserve(video as Element);
};
}, []);
在
IntersectionObserver
的回调函数中,我们检查视频是否进入视口,如果进入视口,首先要加载视频。如果视频加载完成,就开始播放视频:if (entry.isIntersecting) {
// 当视频进入视口时,开始播放视频
if (video?.readyState === 4) {
// 视频已经加载完毕
video?.play();
} else {
// 监听视频加载完成事件
if (video?.dataset.src) {
// 将 data-src 的值赋给 src 属性
video.src = video.dataset.src;
delete video.dataset.src;
video?.addEventListener("loadedmetadata", () => {
video?.play();
});
}
}
} else {
// 当视频离开视口时,暂停视频播放
video?.pause();
}
注意,示例代码中使用了
video?.readyState === 4
来检查视频是否已经加载完毕。readyState
属性表示视频的加载状态,如果它的值为 4,表示视频已经加载完毕。如果视频还没有加载完毕,我们就需要等到它加载完成后再开始播放。为了实现这一点,我们在
data-src
属性中存储视频的地址,然后在视频加载完成后再将它赋值给 src
属性。当视频加载完成后,记得要删除 data-src
属性,避免重复加载视频:if (video?.dataset.src) {
// 将 data-src 的值赋给 src 属性
video.src = video.dataset.src;
delete video.dataset.src;
video?.addEventListener("loadedmetadata", () => {
video?.play();
});
}
下面是最终的代码:
/**
* @description 视频组件
* @param {string} src 视频地址
* @param {number} width 视频宽度
* @param {number} height 视频高度
* @param {string} className 自定义类名
* @returns {JSX.Element}
* @example
* import Video from '@/components/Video';
* <Video src="<https://www.w3schools.com/html/mov_bbb.mp4>" />
*/
import React, { useRef, useEffect } from "react";
interface VideoProps {
src: string;
width?: number | string;
height?: number | string;
className?: string;
}
const Video: React.FC<VideoProps> = ({
src,
width = 400,
height = 300,
className,
}) => {
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
const video = videoRef.current;
const options = {
rootMargin: "0px", // 用于指定目标元素与根元素(视口)的边缘间的偏移量,以便确定何时触发回调函数。
threshold: 0.5, // 指定交叉比例为 50% 时触发回调函数
};
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
// 当视频进入视口时,开始播放视频
if (video?.readyState === 4) {
// 视频已经加载完毕
video?.play();
} else {
// 监听视频加载完成事件
if (video?.dataset.src) {
// 将 data-src 的值赋给 src 属性
video.src = video.dataset.src;
delete video.dataset.src;
video?.addEventListener("loadedmetadata", () => {
video?.play();
});
}
}
} else {
// 当视频离开视口时,暂停视频播放
video?.pause();
}
}, options);
// 监听 video 元素
if (video) {
observer.observe(video);
}
// 组件卸载时取消监听
return () => {
observer.unobserve(video as Element);
};
}, []);
return (
<video
loop
muted
controls
playsInline
width={width}
ref={videoRef}
data-src={src} // 添加 data-src 属性
height={height}
className={className}
/>
);
};
export default Video;