Vue customizes h5video video player progress bar component, which can drag, jump, double speed and full screen

need

A progress bar component controls the playback and progress adjustment of multiple videos. The video can be clicked to watch in full screen, and the only progress bar is the video information of a specified video.

icon

A progress bar controls the playback of multiple videos

accomplish

html

//视频
 <div
        v-for="(item, index) in urls"
        :key="index"
      >
        <video :ref="item.type"  preload="auto" :src="item.url" />
        <div>
        // svg-icon是icon图标组件 
          <svg-icon v-if="!isFullScreen" :style="{ fill: '#dbdbdb', 'font-size': 20 + 'px' }" icon-class="quanping_o" @click="handleFullScreen(item)" />
          <svg-icon v-else :style="{ fill: '#dbdbdb', 'font-size': 20 + 'px' }" icon-class="quxiaoquanping_o" @click="handleFullScreen(item)" />
        </div>
      </div>
//进度条
 <div v-show="fileUrl" class="progress-wrap">
        <div class="operate">
          <div class="speed">
          //倍速列表
            <el-menu class="list" mode="horizontal">
              <el-menu-item v-for="(speed, i) in speedList" :key="i" class="list-item" @click="handleChangeSpeed(speed)">{
   
   { speed }}</el-menu-item>
            </el-menu>
            //显示倍速
            {
   
   { playSpeed }}x
          </div>
          <div>
            <div class="play-wrap" @click="play">
              <svg-icon v-if="!paused" icon-class="zanting" :style="{ 'font-size': 14 + 'px' }" />
              <svg-icon v-else-if="paused" icon-class="bofang" :style="{ 'font-size': 14 + 'px' }" />
            </div>
            // 显示已播放时长和总时长
            <div class="timer">{
   
   { currentTime }} / {
   
   { totalTime }}</div>
          </div>
        </div>
//进度条容器
        <div ref="control" class="control" @click="adjustProgress($event)">
        // 进度条本条
          <div class="progress" :style="{ width: progressWidth }">
          //滑块
            <div class="slider_circle" @mousedown="handleSliderMouseDown" />
            <div class="slider_circle_large" />
          </div>
        </div>
      </div>

vue.js

full screen

 handleFullScreen(item) {
    
    
      this.isFullScreen = !this.isFullScreen //isFullScreen 定义为布尔值,控制样式用的,给视频容器高度设置为100vh,宽度100%实现全屏显示
      this.fullVideoType = item.type  //控制样式用的,不必要
    },

Click on the progress bar to jump

insert image description here

 adjustProgress(e) {
    
    
      e.preventDefault()
      // 这里的this.controlRef = this.$refs.control,加载完视频后定义即可
      const {
    
     left, width } = this.controlRef.getBoundingClientRect()
      // left: 进度条容器control到最左侧的距离,width:容器的宽度
        // e.clientX:鼠标点击的位置到最左侧的距离
      const progressWidth = e.clientX - left
      this.progressWidth = progressWidth + 'px'
      this.updadteCurrentTime(progressWidth, width)
    },

drag the slider

There are detailed explanations of the following parameters in the rookie tutorial. I forgot where to look at this picture. If you claim it, please comment and I will post the link.
insert image description here

  handleSliderMouseDown(event) {
    
    
  //如果不添加以下两处的阻止默认事件,会出现以下情况: 鼠标点击滑块向前拉动,移出进度条范围时,会自动选择文字等元素,出现禁用图标。松开鼠标,再次进入进度条,即使没有按住滑块,滑块也会跟随鼠标移动。这不是我们想要看到的效果。
      event.preventDefault()
      // 滑块点击坐标
      const offsetX = event.offsetX
      document.onmousemove = (e) => {
    
    
        e.preventDefault()
        // 滑动距离可视区域左侧的距离
        const X = e.clientX
        // 减去滑块偏移量
        const cl = X - offsetX
        const {
    
     left, width } = this.controlRef.getBoundingClientRect()
        // 除去滑块按钮长度的进度条长度
        const ml = cl - left
        let progressWidth
        if (ml <= 0) {
    
    
        //进度条长度最小和最大值的界定
          progressWidth = 0
        } else if (ml >= width) {
    
    
          progressWidth = width
        } else {
    
    
          progressWidth = ml
        }
        this.progressWidth = progressWidth + 'px'
        // 更新当前时间
        this.updadteCurrentTime(progressWidth, width)
      }
//抬起鼠标,结束移动事件
      document.onmouseup = () => {
    
    
        document.onmousemove = null
        document.onmouseup = null
      }
    },

double speed

// 倍速
handleChangeSpeed(item) {
this.playSpeed = item
},

play / Pause

 play() {
    
    
 //是否暂停
      this.paused = !this.paused
      // 若此刻状态是重新播放,那么点击播放时,进度条需从头开始前进
      // 进度条控制的是中间视频,this.middleRef
      if (this.middleRef.duration === this.middleRef.currentTime) {
    
    
        this.middleRef.currentTime = 0
        this.currentTime = this.formatSeconds(this.middleRef.currentTime)
        this.progressWidth = 0 + 'px !important'
      }
      if (!this.paused) {
    
    
      // 定时器实时刷新
        this.timer = setInterval(this.updateVideoProgress, 50)
        this.updateVideoProgress()
      }
      this.videoRefArr.forEach((v) => {
    
    
        v.currentTime = this.middleRef.currentTime
        if (this.paused) {
    
    
          v.pause()
        } else {
    
    
          v.play()
          // 按倍速播放
          v.playbackRate = this.playSpeed
        }
      })
    },
    clearTimer() {
    
    
      if (this.timer) {
    
    
        clearInterval(this.timer)
        this.timer = null
      }
    },
      // 更新播放时进度条长度
    updateVideoProgress() {
    
    
    //按照已播放时间和总时长的比例更新进度条长度
      this.progressWidth = (this.middleRef.currentTime / this.middleRef.duration) * 100 + '%'
      this.currentTime = this.formatSeconds(this.middleRef.currentTime)
      this.totalTime = this.formatSeconds(this.middleRef.duration)
    
      // 放完、暂停这两种情况,遍历所有视频,使他们的状态一致,并清除定时器
      if (this.middleRef.currentTime === this.middleRef.duration || this.paused) {
    
    
        this.videoRefArr.forEach((v) => {
    
    
          v.pause()
        })
        this.paused = true
        this.clearTimer()
      }
    },
    // 格式化时间 
    formatSeconds(value) {
    
    
      const result = parseInt(value)
      const h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600)
      const m = Math.floor((result / 60) % 60) < 10 ? '0' + Math.floor((result / 60) % 60) : Math.floor((result / 60) % 60)
      const s = Math.floor(result % 60) < 10 ? '0' + Math.floor(result % 60) : Math.floor(result % 60)
      //把视频时长格式化到毫秒
      let ms
      const msValue = value.toFixed(3).toString().split('.')[1]
      if (Math.floor(msValue % 1000) < 10) {
    
    
        ms = '00' + Math.floor(msValue % 1000)
      } else if (Math.floor(msValue % 1000) > 10 && Math.floor(msValue % 1000) < 100) {
    
    
        ms = '0' + Math.floor(msValue % 1000)
      } else if (Math.floor(msValue % 1000) < 1000) {
    
    
        ms = Math.floor(msValue % 1000)
      }
      let res = ''
      res += `${
      
      h}:`
      res += `${
      
      m}:`
      res += `${
      
      s}.`
      res += `${
      
      ms}`
      return res
    }

After jumping and dragging, the length of the progress bar is updated by updating the currentTime of the video

 // 更新当前时间、帧号
    updadteCurrentTime(progressWidth, width) {
    
    
      this.currentTime = this.formatSeconds((progressWidth / width) * this.middleRef.duration)
      this.totalTime = this.formatSeconds(this.middleRef.duration)
      this.videoRefArr.forEach((v) => {
    
    
        v.currentTime = (progressWidth / width) * this.middleRef.duration
      })
    },

Summary: Such a component needs to be written for internal use in the company. Although less general, it is more flexible. The html structure omits the relevant styles for the sake of intuitive viewing.

Those who are interested can discuss together.

Originality is not easy, please post the source for reprinting

Guess you like

Origin blog.csdn.net/huangyinzhang/article/details/125183579