使用Intersection Observer API實現視頻隊列自動播放

前言

筆者利用空餘時間研究了一下javascriptIntersection Observer API,發現其有很大的應用場景,好比圖片或者內容的懶加載,視差動畫等。筆者也在以前的文章中詳細介紹了3種Observer(觀察者)的用法,包括位置監聽dom變化監聽以及窗口變化監聽,它們有很是多的應用場景,因此頗有必要研究明白, 感興趣的能夠讀完本片文章以後學習一下(幾個很是有意思的javascript知識點總結).javascript

這裏有一個很常見的例子,平時喜歡看短視頻的朋友可能會注意到,咱們在瀏覽某視頻頭條時,滾動視頻列表,當某一個視頻滾動到手機的必定位置時(通常能夠當作是屏幕中心),該視頻會自動播放,當移出指定區域後視頻會自動關閉並播放移入指定區域的下一個視頻,以下: css

做爲一名好奇心極強的前端工程師,有必要好好研究一下其內部實現。

筆者的第一思路就是監聽滾動位置來判斷某個視頻元素是否到達指定區域內,可是這種方式須要處理的條件不少,好比邊界條件判斷,滾動方向判斷等,並且頻繁觸發還會出現性能問題。前端

好在筆者以前深刻研究過Intersection Observer API,發現可使用它提供的API,很方便的監聽到元素在指定根元素下的位置變化,並作一些自定義操做: vue

筆者接下來將直接利用Intersection Observer提供的api來實現視頻在滾動的過程當中自動播放的功能,若是對該api不太熟悉的朋友能夠移步幾個很是有意思的javascript知識點總結java

視頻播放插件筆者將使用比較流行的Dplayer,它能夠很方便的操做視頻的展示並實現很好的排他性播放控制,而且支持彈幕。node

正文

根據以上的介紹咱們大體瞭解了具體的需求,接下來咱們就來基於Intersection Observer API實現一下它。思路大體以下圖所示: react

具體思路就是咱們能夠把 Intersection Observer的根元素的 rootMargin(即根元素的外邊距)設置爲如上圖藍色所示區域,而後當視頻徹底進入該區域內後(也就是 thresholds閾值爲1時),觸發當前視頻的播放便可。由於咱們使用的是 Dplayer,因此咱們只要將其配置屬性中的 mutex屬性設置爲true(爲true時會阻止多個播放器同時播放,當前播放器播放時暫停其餘播放器)。 有關設置 rootMargin的知識,能夠參考下圖介紹:
rootMargin接收格式以下:"10px 0px 10px 0px",從左到右數字依次表明 top(上) right(右) bottom(下) left(左)邊距,固然咱們單位也可使用百分比(%),爲正值時表明擴大更元素的邊距範圍,負值表明縮小根元素的邊距範圍,這裏咱們應該縮小範圍,因此 rootMargin咱們能夠這麼設置"-180px 0px -180px 0px",這樣上下的邊距就會縮小,固然你們也能夠根據需求設置不一樣的值。

有了以上思路以後咱們就能夠實現上文動圖所展現的效果了。筆者將採用react來實現,在實現以前咱們先準備幾個視頻素材,而後實現列表基本框架:webpack

import React, { useEffect, useState } from 'react'
import VideoItem from 'components/VideoItem'
import styles from './videoList.less'

const data = [
    // 視頻列表
]

function VideoList(props) {
  useEffect(() => {
    let observerVideo = new IntersectionObserver(
        (entries, observer) => {
            entries.forEach(entry => {
                // 當移入指定區域內後,播放視頻
                if(entry.intersectionRatio === 1) {
                    // 一些操做
                    return
                }
                // 中止監聽
                // observer.unobserve(entry.target);
              });
            }, 
            {
              root: document.getElementById('scrollView'),
              rootMargin: '-180px 0px -180px 0px',
              threshold: 1
            }
        );
        document.querySelectorAll('.video-item').forEach(video => { observerVideo.observe(video) });
  }, [])
  return <div className={styles.videoWrap}> <div className={styles.list} id="scrollView"> { data.map(item => { return <VideoItem src={item} groupName="video-item" key={item} /> }) } </div> </div> } export default VideoList 複製代碼

以上代碼中VideoItem組件咱們後面會介紹,如今有個問題是咱們已經監聽到了須要自動播放的視頻元素,可是咱們如何通知VideoItem組件讓其播放呢?這裏筆者實現思路是給VideoItem添加一個自定義屬性,該屬性的值就是當前video的src,咱們在監聽到某個視頻元素須要播放時,咱們能夠獲取到以前設置的自定義屬性,而後做爲prop傳給VideoItem,當VideoItem組件監聽到該prop變化時,而且等於自身的src,此時則觸發視頻播放。代碼以下:css3

// VideoItem.js
import React, { useRef, useEffect } from 'react';
import DPlayer from 'dplayer';

export default (props) => {
    let videoRef = useRef(null)
    let dpRef = useRef(null)
    let { src, groupName, curPlaySrc } = props
    useEffect(() => {
        dpRef.current = new DPlayer({
            container: videoRef.current,
            screenshot: true,
            video: {
                url: src,
                thumbnails: 'logo.png'
            },
            logo: 'logo.png'
        });
    }, [])

    useEffect(() => {
        // 噹噹噹前應該播放的視頻url等於當前視頻組件的src時,播放視頻
        if(curPlaySrc === src) {
            dpRef.current.play()
        }
    }, [curPlaySrc])
    return <div data-src={src}> <div ref={videoRef}></div> </div>
}
複製代碼

此時視頻列表頁代碼以下:程序員

// ...
function VideoList(props) {
  const [curPlaySrc, setCurPlaySrc] = useState('')
  useEffect(() => {
    let observerVideo = new IntersectionObserver(
        (entries, observer) => {
            entries.forEach(entry => {
                // 當移入指定區域內後,播放視頻
                if(entry.intersectionRatio === 1) {
                    // 設置當前因該播放的視頻url
                    setCurPlaySrc(entry.target.dataset.src)
                    return
                }
              });
            }, 
            {
              root: document.getElementById('scrollView'),
              rootMargin: '-180px 0px -180px 0px',
              threshold: 1
            }
        );
        document.querySelectorAll('.video-item').forEach(video => { observerVideo.observe(video) });
  }, [])
  return <div className={styles.videoWrap}> <div className={styles.list} id="scrollView"> { data.map(item => { return <VideoItem src={item} groupName="video-item" key={item} curPlaySrc={curPlaySrc} /> }) } </div> </div> } 複製代碼

以上步驟即完成了基於指定區域自動播放視頻的功能,效果以下:

體驗地址

最後

若是想學習更多H5遊戲, webpacknodegulpcss3javascriptnodeJScanvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們的技術羣一塊兒學習討論,共同探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索