前言
业余时间在javascript,学习Intersection Observer API,发现它有很棒的应用场景,比如图片或者内容的偷懒加载,视差动画等等。在前一篇文章中,作者还详细介绍了三种Observer(观察者)的用法,包括位置监听,dom变化监听和窗口变化监听.它们有很多应用场景,因此有必要研究和了解。感兴趣的可以看完这篇文章再学。
这里有一个非常常见的例子。平时喜欢看短视频的朋友可能会注意到,当我们浏览一个视频标题时,我们会滚动浏览视频列表。当一个视频滚动到手机的某个位置(一般可以认为是屏幕的中心)时,视频会自动播放。当视频移出指定区域时,它会自动关闭并播放下一个移入指定区域的视频,如下所示:
作为一个好奇的前端工程师,有必要研究一下它的内部实现。
笔者首先想到的是通过监控滚动位置来判断一个视频元素是否已经到达指定区域,但这种方法需要处理很多条件,比如边界条件判断、滚动方向判断等。频繁触发会导致性能问题。
嗯,我之前深入研究过Intersection Observer API,发现它可以方便地监控指定根元素下元素的位置变化,并做一些自定义操作:
接下来,我将直接使用Intersection Observer提供的api来实现视频在滚动的过程中自动播放.的功能,如果你不熟悉api,可以拿几个有趣的javascript知识总结。
视频插件作者将使用流行的Dplayer,可以轻松操作视频显示,实现良好的独家播放控制,并支持弹幕。
主体
根据上面的介绍,我们对具体的需求有了一个大概的了解,然后我们将基于Intersection Observer API.来实现,思路大致如下图所示:
具体思路是,我们可以将Intersection Observer根元素的rootMargin(即根元素的外边缘距离)设置为上面蓝色所示的区域,然后在视频完全进入该区域时(即thresholds阈值为1时)触发当前视频播放。因为我们使用的是Dplayer,所以我们只需要将它的配置属性中的mutex属性设置为true(如果为true,将会阻止多个玩家同时玩,当前玩家玩的时候其他玩家会被暂停)。有关设置rootMargin,的知识,请参考下图:
rootMargin的接收格式如下:“10px 0px 10px”,从左到右的数字依次表示top(上)、right(右)、bottom(下)和left(左)的边距。当然,我们公司也可以使用百分比(%),这意味着扩大更多元素的保证金范围。负值表示减少根元素的边距范围,这里我们应该减少范围,所以我们可以在rootMargin,设置“-180px 0px -180px 0px”,这样上下边距就会减少。当然,也可以根据需要设置不同的值。
有了以上想法,我们将
可以实现上文动图所展示的效果了。笔者将采用react来实现,在实现之前我们先准备几个视频素材,然后实现列表基本框架: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,此时则触发视频播放。代码如下:
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
以上步骤即完成了基于指定区域自动播放视频的功能,效果如下:
最后
如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》专栏学习讨论,共同探索前端的边界。