
前言
去年利用空余時間研究了一下javascript的Intersection Observer API,發(fā)現(xiàn)其有很大的應用場景,比如圖片或者內(nèi)容的懶加載,視差動畫等。我也在之前的文章中詳細介紹了3種Observer(觀察者)的用法,包括位置監(jiān)聽,dom變化監(jiān)聽以及窗口變化監(jiān)聽,它們有非常多的應用場景,所以很有必要研究明白, 感興趣的可以讀完本片文章之后學習一下(幾個非常有意思的javascript知識點總結(jié)).
這里有一個很常見的例子,平時喜歡看短視頻的朋友可能會注意到,我們在瀏覽某視頻頭條時,滾動視頻列表,當某一個視頻滾動到手機的一定位置時(一般可以看成是屏幕中心),該視頻會自動播放,當移出指定區(qū)域后視頻會自動關(guān)閉并播放移入指定區(qū)域的下一個視頻,如下:
作為一名好奇心極強的前端工程師,有必要好好研究一下其內(nèi)部實現(xiàn)。
我的第一思路就是監(jiān)聽滾動位置來判斷某個視頻元素是否到達指定區(qū)域內(nèi),但是這種方式需要處理的條件很多,比如邊界條件判斷,滾動方向判斷等,而且頻繁觸發(fā)還會出現(xiàn)性能問題。
好在之前深入研究過Intersection Observer API,發(fā)現(xiàn)可以使用它提供的API,很方便的監(jiān)聽到元素在指定根元素下的位置變化,并做一些自定義操作:
接下來我將利用Intersection Observer提供的api來實現(xiàn)視頻在滾動的過程中自動播放的功能,如果對該api不太熟悉的朋友可以移步
幾個非常有意思的javascript知識點總結(jié)
視頻播放插件筆者將使用比較流行的Dplayer,它可以很方便的操作視頻的展現(xiàn)并實現(xiàn)很好的排他性播放控制,并且支持彈幕。
正文
根據(jù)以上的介紹我們大致了解了具體的需求,接下來我們就來基于Intersection Observer API實現(xiàn)一下它。思路大致如下圖所示:
具體思路就是我們可以把Intersection Observer的根元素的rootMargin(即根元素的外邊距)設(shè)置為如上圖藍色所示區(qū)域,然后當視頻完全進入該區(qū)域內(nèi)后(也就是thresholds閾值為1時),觸發(fā)當前視頻的播放即可。因為我們使用的是Dplayer,所以我們只要將其配置屬性中的mutex屬性設(shè)置為true(為true時會阻止多個播放器同時播放,當前播放器播放時暫停其他播放器)。有關(guān)設(shè)置rootMargin的知識,可以參考下圖介紹:
rootMargin接收格式如下:"10px 0px 10px 0px",從左到右數(shù)字依次代表top(上) right(右) bottom(下) left(左)邊距,當然我們單位也可以使用百分比(%),為正值時代表擴大更元素的邊距范圍,負值代表縮小根元素的邊距范圍,這里我們應該縮小范圍,所以rootMargin我們可以這么設(shè)置"-180px 0px -180px 0px",這樣上下的邊距就會縮小,當然大家也可以根據(jù)需求設(shè)置不同的值。
有了以上思路之后我們就可以實現(xiàn)上文動圖所展示的效果了。筆者將采用react來實現(xiàn),在實現(xiàn)之前我們先準備幾個視頻素材,然后實現(xiàn)列表基本框架:
- 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 => {
- // 當移入指定區(qū)域內(nèi)后,播放視頻
- if(entry.intersectionRatio === 1) {
- // 一些操作
- return
- }
- // 停止監(jiān)聽
- // 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組件我們后面會介紹,現(xiàn)在有個問題是我們已經(jīng)監(jiān)聽到了需要自動播放的視頻元素,但是我們?nèi)绾瓮ㄖ猇ideoItem組件讓其播放呢?這里筆者實現(xiàn)思路是給VideoItem添加一個自定義屬性,該屬性的值就是當前video的src,我們在監(jiān)聽到某個視頻元素需要播放時,我們可以獲取到之前設(shè)置的自定義屬性,然后作為prop傳給VideoItem,當VideoItem組件監(jiān)聽到該prop變化時,并且等于自身的src,此時則觸發(fā)視頻播放。代碼如下:
- // 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 => {
- // 當移入指定區(qū)域內(nèi)后,播放視頻
- if(entry.intersectionRatio === 1) {
- // 設(shè)置當前因該播放的視頻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>
- }
以上步驟即完成了基于指定區(qū)域自動播放視頻的功能,效果如下:
體驗地址
視頻自動播放demo
仿微信朋友圈動態(tài)demo
最后
如果想學習更多H5游戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數(shù)據(jù)可視化等前端知識和實戰(zhàn),歡迎在公號《趣談前端》加入我們的技術(shù)群一起學習討論,共同探索前端的邊界。
原文鏈接:https://mp.weixin.qq.com/s/tRTjYlusWOcHEQqbOhQqXw