参考:
H5 video 开发问题及相关知识点
Video元素的使用和常见问题总结
video在安卓与ios实际应用中遇到的问题及解决
autoPlay
自动播放,但是不同机型还需要其他属性配合,比如IOS必须加muted等。建议不使用自动的autoPlay属性,而采用懒加载的方式唤起播放。
readyState属性
只读属性。使用media.readyState返回媒介当前播放位置的就绪状态,共有5个可能值。
HAVE_NOTHING(数字值为0):当前播放位置无有效媒介资源;
HAVE_METADATA(数字值为1):加载中,媒介资源确认存在,但当前位置没有能够加载到有效媒介数据进行播放;
HAVE_CURRENT_DATA(数字值为2):已获取到当前播放数据,但没有足够的数据进行播放;
HAVE_FUTURE_DATA(数字值为3):已获取到后续播放数据,可以进行播放;
HAVE_ENOUGH_DATA(数字值为4):可以进行播放,且浏览器确认媒体数据以某一种速度进行加载,可以保证有足够的后续数据进行播放,而不会使浏览器的播放进度赶上加载数据的末端。
playsInline & webkit-playsinline
标志视频播放时局域播放(不全屏播放),不脱离文档流 。不希望用户来拖动进度条,可以加上playsInline标签,IOS10以下的使用webkit-playsinline。部分机型没有设置playsInLine会自动全屏播放。遇到了再说。
muted
视频里的音频设置。设置后,音频会初始化为静音。默认值是false。IOS必须要加上muted才能自动播放。
controls
加上这个属性,Gecko 会提供用户控制,允许用户控制视频的播放,包括音量,跨帧,暂停/恢复播放。
duration & currentTime
视频总时长以及当前播放时长,可以根据这两个数据得知视频目前的播放进度,做动画设置。
useEffect(() => {
const handleVideoLazyLoad = () => {
const lazyVideos = [].slice.call(document.querySelectorAll('video'));
if (typeof IntersectionObserver !== 'undefined') {
const lazyVideoObserver = new IntersectionObserver((entries) => {
entries.forEach((_video) => {
const video = _video.target;
const isPlaying =
video.currentTime > 0 && !video.paused && !video.ended && video.readyState > video.HAVE_CURRENT_DATA;
if (_video.isIntersecting && !isPlaying) {
const videoSrc = video.getAttribute('src');
const videoPoster = video.getAttribute('poster');
const videoDataSrc = video.getAttribute('data-src');
const videoDataPoster = video.getAttribute('data-poster');
if (videoSrc) {
video.play().catch((e) => {});
} else {
if (!videoPoster && videoDataPoster) video.setAttribute('poster', videoDataPoster);
if (!videoSrc && videoDataSrc) {
video.setAttribute('src', videoDataSrc);
video.play().catch((e) => {});
}
}
} else if (isPlaying) {
video.pause();
// const playtime = video.currentTime;
// const videoDataSrc = video.getAttribute('data-src');
// video.setAttribute('src', '');
// video.load();
// video.setAttribute('src', videoDataSrc);
// video.currentTime = playtime;
}
});
});
lazyVideos.forEach((video) => lazyVideoObserver.observe(video));
}
};
handleVideoLazyLoad();
}, []);
// scroll
const videoPlay = useThrottle(() => {
if (videos.current.every((d) => d.isPlay)) document.removeEventListener('scroll', videoPlay);
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
videos.current.forEach((_v, i) => {
const offsetTop = _v.dom.getBoundingClientRect().top; // video顶部距离视口窗的距离
const top = offsetTop - viewPortHeight; // video顶部距离视窗底部的高度
if (top <= (isMobile ? 150 : 300) && !_v.isPlay) {
_v.dom.play();
_v.isPlay = true;
// _v.dom.style.height = _v.clientHeight !== 0 ? `${_v.clientHeight}px` : `${_v.height}px`;
}
// if(rect.bottom <= 0 && _v.isPlay) {
// _v.dom.pause();
// _v.isPlay = false;
// }
});
}, 300);
const lazyLoadVideos = () => {
const oVideos = document.querySelectorAll('video');
const _videos = [];
oVideos.forEach((dom, i) => {
if (!isEmpty(dom.src)) {
_videos.push({ dom, isPlay: false });
}
});
videos.current = _videos;
document.addEventListener('scroll', videoPlay);
};
useEffect(() => {
lazyLoadVideos();
return () => document.removeEventListener('scroll', videoPlay);
}, []);
只添加autoPlay在IOS无法自动播放。因为IOS有一个策略,静音的视频才能自动播放,也就是muted属性。
// 常用的video标签
某次开发中突然出现的,高度不知为何特别长,后来想了两种解决方案:
width: calc(100% - 48px);
height: calc((100% - 48px) * 16 / 9);
_v.dom.style.height = _v.clientHeight !== 0 ?
${_v.clientHeight}px
:${_v.height}px
;
object.onXXX=function(){myScript};
object.addEventListener(“XXX”, myScript);
document.querySelector(‘video’).pause()
document.querySelector(‘video’).pause()
document.querySelector(‘video’).load()
当音频/视频处于加载过程中时,会依次发生以下事件:
loadstart
durationchange
loadedmetadata
loadeddata
progress
canplay
canplaythrough
useEffect(() => {
querySelector('#testVideo').then((video) => {
video.addEventListener('canplay', (e) => {
console.log('canplay', e);
});
video.addEventListener('pause', (e) => {
console.log('pause', e);
});
video.addEventListener('ended', (e) => {
console.log('ended', e);
});
video.addEventListener('play', (e) => {
console.log('play', e);
});
video.addEventListener('load', (e) => {
console.log('load', e);
});
video.addEventListener('progress', (e) => {
console.log('progress', e);
});
video.addEventListener('suspend', (e) => {
console.log('suspend', e);
});
});
}, []);
requestAnimationFrame
方式。currentTime / duration
,然后根据progress进度处理动画。就算视频不播放/暂停等状态,也可以从progress上精确知道。 useEffect(() => {
cancelAnimationFrame(timer.current);
timer.current = requestAnimationFrame(animationVideo);
return () => cancelAnimationFrame(timer.current);
}, [index, animationVideo]);
// 动画跟随进度处理
// videoDomMap[index]是当前活跃元素
const animationVideo = useCallback(() => {
let percentage = 0;
if (videoDomMap[index]?.duration) {
if (lineWidth[index - 2]?.style) {
percentage = videoDomMap[index].currentTime / videoDomMap[index].duration;
lineWidth[index - 2].style.width = `${percentage * 100}%`;
}
}
if (percentage >= 1) {
// 播放完毕,切换下一个
changeVideo(index+1)
}
cancelAnimationFrame(timer.current);
timer.current = requestAnimationFrame(animationVideo);
});