React+TS+IntersectionObserver實(shí)現(xiàn)視頻懶加載和自動(dòng)播放功能
為了提高用戶(hù)體驗(yàn),網(wǎng)站通常會(huì)使用視頻來(lái)呈現(xiàn)內(nèi)容。然而,在網(wǎng)站中使用自動(dòng)播放的視頻通常會(huì)導(dǎo)致頁(yè)面加載時(shí)間變長(zhǎng),因?yàn)橛脩?hù)需要等待更長(zhǎng)的時(shí)間才能看到內(nèi)容。為了解決這個(gè)問(wèn)題,我們可以使用懶加載技術(shù)來(lái)延遲視頻的加載,只有在用戶(hù)滾動(dòng)頁(yè)面并且視頻進(jìn)入視口時(shí)才開(kāi)始下載視頻資源。同時(shí),為了進(jìn)一步提高用戶(hù)體驗(yàn),當(dāng)視頻進(jìn)入用戶(hù)的視野范圍時(shí),我們希望視頻能夠自動(dòng)播放。
本文將介紹如何使用 React + TypeScript 來(lái)通過(guò) IntersectionObserver API 實(shí)現(xiàn)這一功能。在正式開(kāi)始之前,我們先來(lái)看一下最終實(shí)現(xiàn)的效果圖:
實(shí)現(xiàn)這一功能的核心在于 IntersectionObserver API。下面我們將簡(jiǎn)單介紹一下這個(gè) API 的用法。
IntersectionObserver
IntersectionObserver 是瀏覽器內(nèi)置的 API,用于監(jiān)聽(tīng)元素是否交叉或超出了視口(viewport)的變化。這個(gè) API 提供了一種異步觀(guān)察目標(biāo)元素的機(jī)制,在元素進(jìn)入或離開(kāi)視口時(shí)觸發(fā)回調(diào)函數(shù)。
以下是 IntersectionObserver API 的基本語(yǔ)法:
const observer = new IntersectionObserver(callback, options);
其中 callback
是回調(diào)函數(shù),options
是配置選項(xiàng),用于指定觀(guān)察器的參數(shù),如視口的大小、元素與視口的交叉比例等。
創(chuàng)建 IntersectionObserver 對(duì)象后,我們就可以監(jiān)聽(tīng)目標(biāo)元素:
observer.observe(target); // target 是被觀(guān)察的目標(biāo)元素
組件卸載時(shí),應(yīng)取消監(jiān)聽(tīng):
observer.unobserve(target);
如果監(jiān)聽(tīng)了多個(gè)元素,可以停止監(jiān)聽(tīng)所有目標(biāo)元素:
observer.disconnect();
上面的 disconnect()
方法用于停止所有目標(biāo)元素的監(jiān)聽(tīng),并銷(xiāo)毀 IntersectionObserver 對(duì)象。
現(xiàn)在,你應(yīng)該已經(jīng)了解了 IntersectionObserver API。下面,我們將通過(guò)編寫(xiě)一個(gè)可擴(kuò)展的組件來(lái)實(shí)現(xiàn)效果圖演示的功能。
視頻播放控制組件
首先,我們定義一個(gè) VideoProps
接口,它包含了我們的 Video
組件的屬性:
interface VideoProps { src: string; width?: number; height?: number; className?: string; }
接下來(lái),我們定義 Video
組件,它接收 src
、width
、height
和 className
屬性:
const Video: React.FC<VideoProps> = ({ src, width = 400, height = 300, className, }) => { // ... };
在 Video
組件中,我們通過(guò) useRef
鉤子來(lái)創(chuàng)建一個(gè) videoRef
引用。我們將通過(guò)該引用來(lái)判斷視頻狀態(tài)、更新視頻真實(shí)地址、控制視頻的播放和暫停功能。
const videoRef = useRef<HTMLVideoElement>(null);
然后,我們使用 useEffect
鉤子來(lái)創(chuàng)建一個(gè) IntersectionObserver
實(shí)例。我們要實(shí)現(xiàn)的功能主要就是通過(guò)這個(gè)實(shí)例來(lái)實(shí)現(xiàn)的。
useEffect(() => { const video = videoRef.current; const options = { rootMargin: "0px", threshold: 0.5, // 指定交叉比例為 50% 時(shí)觸發(fā)回調(diào)函數(shù) }; // 創(chuàng)建 IntersectionObserver 實(shí)例 const observer = new IntersectionObserver(([entry]) => { // ... }, options); // 監(jiān)聽(tīng) video 元素 if (video) { observer.observe(video); } // 組件卸載時(shí)取消監(jiān)聽(tīng) return () => { observer.unobserve(video as Element); }; }, []);
在 IntersectionObserver
的回調(diào)函數(shù)中,我們檢查視頻是否進(jìn)入視口,如果進(jìn)入視口,首先要加載視頻。如果視頻加載完成,就開(kāi)始播放視頻:
if (entry.isIntersecting) { // 當(dāng)視頻進(jìn)入視口時(shí),開(kāi)始播放視頻 if (video?.readyState === 4) { // 視頻已經(jīng)加載完畢 video?.play(); } else { // 監(jiān)聽(tīng)視頻加載完成事件 if (video?.dataset.src) { // 將 data-src 的值賦給 src 屬性 video.src = video.dataset.src; delete video.dataset.src; video?.addEventListener("loadedmetadata", () => { video?.play(); }); } } } else { // 當(dāng)視頻離開(kāi)視口時(shí),暫停視頻播放 video?.pause(); }
注意,示例代碼中使用了 video?.readyState === 4
來(lái)檢查視頻是否已經(jīng)加載完畢。readyState
屬性表示視頻的加載狀態(tài),如果它的值為 4,表示視頻已經(jīng)加載完畢。
如果視頻還沒(méi)有加載完畢,我們就需要等到它加載完成后再開(kāi)始播放。為了實(shí)現(xiàn)這一點(diǎn),我們?cè)?data-src
屬性中存儲(chǔ)視頻的地址,然后在視頻加載完成后再將它賦值給 src
屬性。當(dāng)視頻加載完成后,記得要?jiǎng)h除 data-src
屬性,避免重復(fù)加載視頻:
if (video?.dataset.src) { // 將 data-src 的值賦給 src 屬性 video.src = video.dataset.src; delete video.dataset.src; video?.addEventListener("loadedmetadata", () => { video?.play(); }); }
我們看下加載視頻的效果圖:
在效果圖中可以看到,當(dāng)視頻進(jìn)入視口后,開(kāi)始下載視頻。視頻下載完成后便開(kāi)始自動(dòng)播放。當(dāng)視頻移出視口時(shí),視頻會(huì)自動(dòng)暫停。
至此,這個(gè)組件基本就完成了。下面是最終的代碼:
/** * @description 視頻組件 * @param {string} src 視頻地址 * @param {number} width 視頻寬度 * @param {number} height 視頻高度 * @param {string} className 自定義類(lèi)名 * @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", // 用于指定目標(biāo)元素與根元素(視口)的邊緣間的偏移量,以便確定何時(shí)觸發(fā)回調(diào)函數(shù)。 threshold: 0.5, // 指定交叉比例為 50% 時(shí)觸發(fā)回調(diào)函數(shù) }; // 創(chuàng)建 IntersectionObserver 實(shí)例 const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { // 當(dāng)視頻進(jìn)入視口時(shí),開(kāi)始播放視頻 if (video?.readyState === 4) { // 視頻已經(jīng)加載完畢 video?.play(); } else { // 監(jiān)聽(tīng)視頻加載完成事件 if (video?.dataset.src) { // 將 data-src 的值賦給 src 屬性 video.src = video.dataset.src; delete video.dataset.src; video?.addEventListener("loadedmetadata", () => { video?.play(); }); } } } else { // 當(dāng)視頻離開(kāi)視口時(shí),暫停視頻播放 video?.pause(); } }, options); // 監(jiān)聽(tīng) video 元素 if (video) { observer.observe(video); } // 組件卸載時(shí)取消監(jiān)聽(tīng) 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;
結(jié)語(yǔ)
通過(guò)本文的介紹,我們學(xué)習(xí)了如何使用 React + TypeScript 和 IntersectionObserver API 來(lái)實(shí)現(xiàn)一個(gè)視頻播放控制組件。該組件具有懶加載功能,只有在用戶(hù)滾動(dòng)頁(yè)面且視頻進(jìn)入視口時(shí)才開(kāi)始下載視頻資源。同時(shí),當(dāng)視頻進(jìn)入用戶(hù)的視野范圍時(shí),視頻能夠自動(dòng)播放,提高了用戶(hù)的使用體驗(yàn)。
IntersectionObserver API 提供了一種異步觀(guān)察目標(biāo)元素的機(jī)制,即在元素進(jìn)入或離開(kāi)視口時(shí)觸發(fā)回調(diào)函數(shù)。這為我們的視頻播放控制組件提供了關(guān)鍵的功能。
通過(guò)編寫(xiě)這個(gè)可擴(kuò)展的組件,你可以為網(wǎng)站的視頻播放功能提供更好的用戶(hù)體驗(yàn)。同時(shí),我們也了解了如何使用 React + TypeScript 和 IntersectionObserver API 來(lái)實(shí)現(xiàn)類(lèi)似的功能,這將為我們今后的開(kāi)發(fā)工作提供更多的思路和幫助。
到此這篇關(guān)于React+TS+IntersectionObserver實(shí)現(xiàn)視頻懶加載和自動(dòng)播放!的文章就介紹到這了,更多相關(guān)React懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決React報(bào)錯(cuò)Rendered more hooks than during
這篇文章主要為大家介紹了React報(bào)錯(cuò)Rendered more hooks than during the previous render解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12教你使用vscode 搭建react-native開(kāi)發(fā)環(huán)境
本文記錄如何使用vscode打造一個(gè)現(xiàn)代化的react-native開(kāi)發(fā)環(huán)境,旨在提高開(kāi)發(fā)效率和質(zhì)量。本文給大家分享我遇到的問(wèn)題及解決方法,感興趣的朋友跟隨小編一起看看吧2021-07-07next-redux-wrapper使用細(xì)節(jié)及源碼分析
這篇文章主要為大家介紹了next-redux-wrapper使用細(xì)節(jié)及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02ReactNative實(shí)現(xiàn)圖片上傳功能的示例代碼
本篇文章主要介紹了ReactNative實(shí)現(xiàn)圖片上傳功能的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-07-07使用react在修改state中的數(shù)組和對(duì)象數(shù)據(jù)的時(shí)候(setState)
這篇文章主要介紹了使用react在修改state中的數(shù)組和對(duì)象數(shù)據(jù)的時(shí)候(setState),具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09詳解React setState數(shù)據(jù)更新機(jī)制
這篇文章主要介紹了React setState數(shù)據(jù)更新機(jī)制的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用React框架,感興趣的朋友可以了解下2021-04-04react native實(shí)現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實(shí)例
下面小編就為大家?guī)?lái)一篇react native實(shí)現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08