基于 React video 视频打点

HTML  元素用于在HTML或者XHTML文档中嵌入媒体播放器,用于支持文档内的视频播放。

  • currentTime:读取 CurentTime 返回一个双精度浮点值,指示以秒为单位的媒体的当前播放位置。如果video尚未开始播放,则会在开始播放后返回偏移量。通过 CurentTime 将当前播放位置设置为给定时间,会在加载媒体时将媒体查找到该位置(从指定的位置开始播放)。* duration(只读):一个双精度浮点值,它指示媒体的持续时间(总长度),以秒为单位,在媒体的时间线上。* volume:音量* playbackrate:播放速度* play():播放视频* pause():暂停视频更多属性:developer.mozilla.org/zh-CN/docs/…

实现思路:

1.播放 / 暂停

// 获取 dom 节点
const player = document.querySelector('.player')
const video = player.querySelector('.viewer')
const toggle = player.querySelector('.toggle') 

当 video 标签和 控制按钮被点击时,切换播放状态

video.addEventListener('click', togglePlay)
toggle.addEventListener('click', togglePlay) 

判断 video 的状态,如果当前是暂停,则调用 video 的 play。

// 切换播放状态
function togglePlay() {const method = video.paused ? 'play' : 'pause'video[method]()
} 

2.切换播放状态图标,对 video 进行播放状态的监听

video.addEventListener('play', updatedToggle)
video.addEventListener('pause', updatedToggle) 
// 切换播放状态图标
function updatedToggle() {const icon = video.paused ? '►' : '❚ ❚'toggle.textContent = icon
} 

3.实现前进后退功能

获取页面上的两个前进后退按钮,为其添加 click 事件。

const skipButtons = player.querySelectorAll('[data-skip]')

skipButtons.forEach(skipButton => skipButton.addEventListener('click', skip)) 

获取 skip 按钮上的自定义属性,改变 video 的 currentTime

function skip() {video.currentTime += parseFloat(this.dataset.skip)
} 

4.改变播放速度和音量

获取页面上控制音量和播放速度的两个按钮,为其添加 change 事件和 mousemove 事件,实现点击或拖动改变的效果。

const ranges = player.querySelectorAll('.player__slider')

ranges.forEach(range => range.addEventListener('change', handleRangeUpdate))
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate))

// 改变播放速度和音量
function handleRangeUpdate() {video[this.name] = this.value
} 

5.实现播放进度条的颜色移动

const progressBar = player.querySelector('.progress__filled') 

监听 video 标签的 timeupdate

video.addEventListener('timeupdate', handleProgress) 

用当前播放的时间 video.currentTime 除以视频的总时长 video.duration

// 改变进度条
function handleProgress() {const percent = (video.currentTime / video.duration) * 100;progressBar.style.flexBasis = `${percent}%`;
} 

6.拖动进度条,视频切换到对应的播放时间

const progress = player.querySelector('.progress') 

需要一个变量,控制切换是否生效

let mousedown = false 

监听 progress 的 clickmousemovemousedownmouseup 四个事件

let mousedown = false
progress.addEventListener('click', scrub)
progress.addEventListener('mousemove', (e) => mousedown && scrub(e))
progress.addEventListener('mousedown', () => mousedown = true)
progress.addEventListener('mouseup', () => mousedown = false) 
基于 React video 视频打点_第1张图片
// 点击切换播放进度
function scrub(e) {const scrubTime = (e.offsetX / progress.offsetWidth) * video.durationvideo.currentTime = scrubTime
} 
基本思路:

隐藏原生 video 控件,实现原生控件的功能。

 

传入的 controls 的是控件的数量,通过该数组来控制显示。

播放 / 暂停

{controls.includes('play') ? ({isPlaying ? 'Pause' : 'Play'}
) : null} 

显示播放时间

对传入的时间做一些格式化操作

const getTimeCode = (secs: number): string => {let secondsNumber = secs ? parseInt(String(secs), 10) : 0;let hours = Math.floor(secondsNumber / 3600);let minutes = Math.floor((secondsNumber - hours * 3600) / 60);let seconds = secondsNumber - hours * 3600 - minutes * 60;let hoursStr: string = String(hours);let minutesStr: string = String(minutes);let secondsStr: string = String(seconds);if (hours < 10) {hoursStr = '0' + hours;}if (minutes < 10) {minutesStr = '0' + minutes;}if (seconds < 10) {secondsStr = '0' + seconds;}return `${hoursStr !== '00' ? hoursStr + ':' : ''}${minutesStr}:${secondsStr}`;
};

const durationTimeCode = getTimeCode(Math.ceil(duration));
const currentTimeCode =currentTime !== duration ? getTimeCode(currentTime) : durationTimeCode; 
{controls.includes('time') ? (
{currentTimeCode}/{durationTimeCode}
) : null}

进度条组件,通过 ref 向父组件暴露子组件的引用。Marker 是标记点组件,接收传入的数组,向外暴露 onMarkerClick 事件。

{controls.includes('progress') ? (
0% played{markers &&markers.map((marker, index) => {return ();})}
) : null}

Marker 组件本质上是有 i 标签组成的一个个小点,通过传入的 duration 和 time 计算出 position 的值。

if (duration) {const percent = time <= duration ? time / duration : 1;return `calc(${percent * 100}% - 2px)`;
} 
 {onMarkerClick(marker);}}
/> 

控制音量组件

{controls.includes('volume') ? (
{volume * 100}% volumeVolume
) : null}

全屏组件

{controls.includes('full-screen') ? (
) : null} 

进度条事件如下:

  • play(点击播放)
  • pause(点击暂停)
  • handleProgressClick(进度条点击事件)
  • handleVolumeClick(改变音量事件)
  • handleMuteClick(点击静音事件)
  • handleFullScreenClick(点击全屏事件)
  • handleMarkerClick(标记点事件)

Controls 组件通过 isPlaying 来判断当前的播放状态。当触发 play 和 pause 事件时,应当改变 isPlaying 的值。

play 和 pause

// play
const play = () => {if (!videoRef.current) return;const playPromise = videoRef.current.play();playPromise &&playPromise.then(() => { }).catch(e => {console.log('Operation is too fast, audio play fails');});setIsPlaying(true);
};

// pause
const pause = () => {if (!videoRef.current) return;videoRef.current.pause();setIsPlaying(false);
}; 

handleProgressClick

点击进度条,期望就是 video 的时间可以到达点击的位置。

  • e.clientX:鼠标相对于浏览器窗口可视区域的X,Y坐标,可视区域不包括工具栏和滚动条。
  • element.getBoundingClientRect():返回的结果是包含完整元素的最小矩形,并且拥有left, top, right, bottom, x, y, width, 和 height这几个以像素为单位的只读属性用于描述整个边框。除了widthheight 以外的属性是相对于视图窗口的左上角来计算的。
基于 React video 视频打点_第2张图片
  • 获取当前页面滚动条横坐标的位置:document.body.scrollLeft
基于 React video 视频打点_第3张图片
const handleProgressClick = (e: Event) => {const x =e['clientX'] -progressEl.current!.getBoundingClientRect().left +document.body.scrollLeft;const percentage =(x * progressEl.current!.max) / progressEl.current!.offsetWidth;videoRef.current!.currentTime =(percentage / 100) * videoRef.current!.duration;
}; (percentage / 100) * videoRef.current!.duration; }; 

handleVolumeClick

音量的改变与进度条同理。当改变的音量的时候,静音状态应该设置为 false。

const handleVolumeClick = e => {const y =volumeEl.current!.offsetWidth -(e.clientY -volumeEl.current!.getBoundingClientRect().top +document.body.scrollTop);const percentage =(y * volumeEl.current!.max) / volumeEl.current!.offsetWidth;videoRef.current!.muted = false;const volume = videoRef.current.volume;onVolumechange?.(volume);setVolume(percentage / 100);
}; 

handleMuteClick,设置 muted 的状态

const handleMuteClick = () => {if (muted) {videoRef.current!.muted = false;setVideoMuted(false);} else {videoRef.current!.muted = true;setVolume(0);setVideoMuted(true);}
}; 

handleFullScreenClick

通过 isFullScreen 来控制是否全屏。点击全屏的时候,调用当前元素上的 requestFullscreen 方法,退出全屏时,调用 document 的 exitFullscreen 方法。

const handleFullScreenClick = () => {const videoWrap = document.getElementsByClassName('react-video-wrap')[0];if (isFullScreen) {document.body.classList.remove('react-video-full-screen');if (document['exitFullscreen']) {document['exitFullscreen']();} else if (document['mozCancelFullScreen']) {document['mozCancelFullScreen']();} else if (document['webkitExitFullscreen']) {document['webkitExitFullscreen']();} else if (document['msExitFullscreen']) {document['msExitFullscreen']();}} else {document.body.classList.add('react-video-full-screen');if (videoWrap['requestFullscreen']) {videoWrap['requestFullscreen']();} else if (videoWrap['mozRequestFullScreen']) {videoWrap['mozRequestFullScreen']();} else if (videoWrap['webkitRequestFullscreen']) {videoWrap['webkitRequestFullscreen']();} else if (videoWrap['msRequestFullscreen']) {videoWrap['msRequestFullscreen']();}}setIsFullScreen(!isFullScreen);
}; 

handleMarkerClick

点击标记点,进度条到达标记点的时间

const handleMarkerClick = (marker: object) => {videoRef.current!.currentTime = marker['time'];onMarkerClick(marker);
}; 

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(react.js,音视频,javascript)