阅读 21296

浏览器中实现视频播放器的基本思路

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

自定义个播放器,组件都是用的原生的,所以有点丑,重点关注业务逻辑吧。

界面大概长下面这个样子。 大家可以看着界面,在脑海中想一下自己会如何实现这个视频播放器。可以问自己以下几个问题:

  • 这个组件会接受那些props
  • 如何获取视频的基本信息,包括时长,分辨率等
  • 暂停、播放如何实现
  • 拖动进度条的逻辑如何实现
  • 视频初始加载显示loading如何处理
  • 视频播放过程中卡顿显示loading如何处理

如果对于浏览器音视频相关的API不清楚,不知道如何下手,参考下我的这篇文章:浏览器中的音视频知识

image.png

整体思路如下:

  1. 该组件接收一个视频的src作为参数
  2. 监听onLoadedMetadata事件,获取视频时长(duration),真实宽高(videoWidth,videoHeight)
  3. 点击播放/暂停时,调用视频元素的play/pause方法
  4. 播放时,监听onTimeUpdate,获取当前播放时间(currentTime),计算出进度条的进度
  5. 拖动进度条,设置视频当前播放时间,与步骤4相反
  6. 视频初始加载时,显示loading。即组件初次渲染时,loading属性默认为true,即显示加载效果,当视频元数据加载时,取消loading
  7. 视频卡顿时,显示loading。该功能的实现是监听的onWaiting事件,不卡顿时,取消loading,监听的是onCanPlay事件

代码实现

代码是用React实现的,用Vue也是一样的,关注业务逻辑即可。如果大家有需求,我可以再更新下这篇文章,添加上Vue的代码。

注意:dom原生事件在react中需要前面加个on,然后写成驼峰的形式

function formatDuration(duration) {
    var sec_num = parseInt(duration, 10); // don't forget the second param
    var hours   = Math.floor(sec_num / 3600);
    var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
    var seconds = sec_num - (hours * 3600) - (minutes * 60);

    if (hours   < 10) {hours   = "0"+hours;}
    if (minutes < 10) {minutes = "0"+minutes;}
    if (seconds < 10) {seconds = "0"+seconds;}
    return hours+':'+minutes+':'+seconds;
}

import React, { createRef, useState } from 'react'
import './VideoPlayer.css'
function VideoPlayer({src}){
    const videoDom=createRef()
    // 视频当前播放时间
    const [curTime,setCurTime]=useState(0)
    // 视频时长
    const [duration,setDuration]=useState(0)
    // 视频状态,是否暂停
    const [isPause,setPause]=useState(true)
    // 视频真是尺寸
    const [size,setSize]=useState({width:1920,height:1080})
    // 视频加载中
    const [waiting,setWaiting]=useState(true)

    // 视频元数据加载成功
    const onLoad=(e)=>{
        const {duration,videoWidth,videoHeight}=e.target
        setDuration(duration)
        setSize({width:videoWidth,height:videoHeight})
        setWaiting(false)
    }
    // 控制播放暂停
    const handlePlay=(play)=>{
        const v=videoDom.current
        if(play){
            setPause(false)
            v.play()
        }else{
            setPause(true)
            v.pause()
        }

    }
    // 拖动slider时改变视频currentTime
    const onSliderChange=(e)=>{
        setCurTime(e.target.value)
        videoDom.current.currentTime=e.target.value

    }
    // 监听video timeupdate
    const onTimeUpdate=()=>{
        const v=videoDom.current
        setCurTime(v.currentTime)
        if(v.ended){
            handlePlay(false)
            v.currentTime=0
        }
    }
    // 卡顿时,显示加载中提示
    const onWaiting=()=>{
        setWaiting(true)
    }
    // 可以播放时,隐藏加载中提示
    const onCanPlay=()=>{
        setWaiting(false)
    }
    return <div className="video-wrapper">
            <video ref={videoDom}  src={src} onLoadedMetadata={onLoad} onTimeUpdate={onTimeUpdate} onWaiting={onWaiting} onCanPlay={onCanPlay}  ></video>
            {/* 视频加载时显示loading */}
            {waiting && <div className="waiting">loading...</div>}
            <div className="video-controls">
                {/* 播放按钮 */}
                {isPause? <button onClick={()=>{handlePlay(true)}}>播放</button>: <button onClick={()=>{handlePlay(false)}}>暂停</button>}
                {/* 进度条 */}
                <input type="range" min="0" max={duration}  value={curTime} onChange={onSliderChange}/>
                {/* 时间信息和分辨率信息 */}
                <span>{formatDuration(curTime)}/{formatDuration(duration)}</span>
                <span>分辨率:{size.width}x{size.height}</span>
            </div>
        </div>
}
export default VideoPlayer

复制代码

样式写了一点点,VideoPlayer.css

.video-wrapper{
    width:800px;
}
.video-wrapper>video{
    width: 100%;
}

.video-controls{
    margin-top: 20px;
}
复制代码

本文的重点不在于React,React只是一个载体,同样的逻辑可以很容易地用Vue实现,重点在于自定义一个视频播放器的逻辑。

文章分类
前端
文章标签