1. 需求背景
前段时间接到一个需求,运营希望该
h5
页面能够实现视频进入视野自动播放,就像下面这样:
这能忍,不能忍又咋整,谁让她是需求方呢。因为
video
坑点还是蛮多的,很多webview
不支持自动播放,安卓设备种类那么多,即便用hack
的方法实现了,也不能保证所有设备都兼容。
最后,采用了折中的方案:端内支持
video
进入视口自动播放(因为两端webview
都支持自动播放),端外可以不支持。
2. 实现
要实现该功能,最方便的方法当然是使用
IntersectionObserver
啦。先看一下该API
的兼容性:
兼容还不是很好,生产环境下还是得引入
polyfill
:github.com/w3c/Interse…
IntersectionObserver
IntersectionObserver
可用于判断某个元素是否进入了"视口"。所以可以在元素进入到视口以后执行我们的回调函数。
对于该
API
不熟悉的可以移步IntersectionObserver API 使用教程-阮一峰先学习一下。
思路
- 首先需要把每一个模块都监听起来;
- 当模块完全进入视口以后就让该模块内的
video
播放。
开干
demo
的html
和css
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
font-size: 100px;
padding: .1rem;
overflow-x: hidden;
background: linear-gradient(thistle, wheat);
}
.title {
font-size: .22rem;
margin: 50px auto;
color: seagreen;
text-align: center;
border: .02rem dashed sienna;
}
.video-box {
width: 100%;
height: 4rem;
border: .04rem solid;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: .5rem;
}
.one {
border-color: seagreen;
}
.two {
border-color: skyblue;
}
.three {
border-color: thistle;
}
.four {
border-color: tomato;
}
.five {
border-color: rosybrown;
}
.video-box>p {
font-size: .2rem;
color: sienna;
margin-bottom: .2rem;
}
video {
width: 2.8rem;
height: 1.8rem;
border-radius: .1rem;
object-fit: cover;
}
</style>
</head>
<body>
<h2 class="title">h5视频进入视口自动播放Demo</h2>
<section class="video-box one">
<p>模块一</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box two">
<p>模块二</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box three">
<p>模块三</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box four">
<p>模块四</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box five">
<p>模块五</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
</body>
</html>
为了模拟出现在视口自动播放的效果,页面总共有五个视频模块:
下面就需要补上js
,完成画龙点睛之笔:
- 监听所有的模块,即
video-box
类:
function arrayLikeToArray(arrayLike) {
return Array.from(arrayLike);
};
const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
// 监听所有的模块Dom
const elementsObserve = new IntersectionObserver(entries => {
entries.forEach(item => {
// 如果模块出现在视口内
if (item.isIntersecting) {
const video = item.target.getElementsByTagName('video')[0];
video.play();
}
})
}, {
// 此处表示当前被监听元素完全出现在视口时再触发上面的回调:即播放模块内的视频
threshold: [1],
});
sectionList.forEach(item => {
elementsObserve.observe(item);
});
完成上面的代码,视频就能够实现出现在视口后自动播放,但是上面的代码可能会(不同浏览器/webview
下video
同时播放时限制不一样)出现页面多个视频在同时播放。
- 每次只允许一个视频在播放
// +
const videoList = arrayLikeToArray(document.querySelectorAll('video'));
function onlyOneVideoCanPlay(video) {
videoList.forEach(item => {
if (item === video) {
item.play();
} else {
item.pause();
}
})
}
function arrayLikeToArray(arrayLike) {
return Array.from(arrayLike);
};
const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
const elementsObserve = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
const video = item.target.getElementsByTagName('video')[0];
// -
onlyOneVideoCanPlay(video);
}
})
}, {
threshold: [1],
});
sectionList.forEach(item => {
elementsObserve.observe(item);
});
效果:
在
chrome
下需要先手动点击播放一个视频后再滑动。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
font-size: 100px;
padding: .1rem;
overflow-x: hidden;
background: linear-gradient(thistle, wheat);
}
.title {
font-size: .22rem;
margin: 50px auto;
color: seagreen;
text-align: center;
border: .02rem dashed sienna;
}
.video-box {
width: 100%;
height: 4rem;
border: .04rem solid;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: .5rem;
}
.one {
border-color: seagreen;
}
.two {
border-color: skyblue;
}
.three {
border-color: thistle;
}
.four {
border-color: tomato;
}
.five {
border-color: rosybrown;
}
.video-box>p {
font-size: .2rem;
color: sienna;
margin-bottom: .2rem;
}
video {
width: 2.8rem;
height: 1.8rem;
border-radius: .1rem;
object-fit: cover;
}
</style>
</head>
<body>
<h2 class="title">h5视频进入视口自动播放Demo</h2>
<section class="video-box one">
<p>模块一</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box two">
<p>模块二</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box three">
<p>模块三</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box four">
<p>模块四</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
<section class="video-box five">
<p>模块五</p>
<video
controls
loop="loop"
x5-playsinline
playsinline="true"
x-webkit-airplay="allow"
webkit-playsinline="true"
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
poster="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg"
src="https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/xgplayer-demo-720p.mp4"
></video>
</section>
</body>
<script>
function arrayLikeToArray(arrayLike) {
return Array.from(arrayLike);
}
function commonIntersection(elements, cb) {
const elementsObserve = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
cb(item);
}
})
}, {
threshold: [1],
});
if (Array.isArray(elements)) {
elements.forEach(item => {
elementsObserve.observe(item);
})
} else {
elementsObserve.observe(elements);
}
}
function setVideoAutoPlay() {
// 视频出现在屏幕中间自动播放
const sectionList = arrayLikeToArray(document.querySelectorAll('.video-box'));
commonIntersection(sectionList, item => {
const video = item.target.getElementsByTagName('video')[0];
onlyOneVideoCanPlay(video);
});
}
const videoList = arrayLikeToArray(document.querySelectorAll('video'));
function onlyOneVideoCanPlay(video) {
videoList.forEach(item => {
if (item === video) {
item.play();
} else {
item.pause();
}
})
}
setVideoAutoPlay();
</script>
</html>