Пользовательский видеоплеер H5

JavaScript
Пользовательский видеоплеер H5

Тег видео используется в качестве видеоплеера на работе, а вместо него в начале используется плагин video.js.Если только этот плагин использовать для простого воспроизведения видео, это немного расточительно, а после этого плагина -in упакован с webpack, vendor.js будет очень большим, поэтому в этой статье реализован пользовательский видеоплеер на основе тега HTML5 video. Среди них реализованы такие функции, как пауза воспроизведения, перетаскивание прогресса, регулировка громкости и полноэкранный режим.

1. Предварительный просмотр эффекта

2. Введение в шаги

  1. Элемент DOM и написание стилей css;

  2. простое воспроизведение и пауза;

  3. Отображение прогресса времени;

  4. Щелкните и перетащите полосу управления видео;

  5. Звуковой индикатор прогресса;

  6. Полноэкранный режим и выход из полноэкранного режима, базовая реализация;

  7. Панель управления скрыта или показана;

  8. Видео поставляется со свойствами и методами для организации:

    свойство или метод объяснять
    currentTime Текущее время воспроизведения видео, единица измерения с
    duration Общее время воспроизведения текущего видео, ед.
    volume звук, минимальное значение 0, максимальное значение 1
    paused приостановленное состояние
    ended конечное состояние
    play() проиграть видео
    pause() Приостановить воспроизведение видео
    loadedmetadata() Загрузка видео для получения данных, здесь для получения продолжительности
    timeupdate() Событие изменения видео, получайте текущее время в реальном времени здесь
    ended() Событие окончания воспроизведения видео
    volumechange() видео звук событие

3. Шаги к реализации

1. Написание статических страниц

В этом примере используются скаффолдинг vue, а конкретные элементы DOM реализованы следующим образом и добавляют соответствующие комментарии:

<div class="custom-video_container">
    <!--video-->
    <video 
        class="custom-video_video"
        ref="custom-video"
    >
      <source type="video/mp4">
      <p>设备不支持</p>
    </video>
    <!--播放或者暂停按钮-->
    <span class="custom-video_play custom-video_play-pause iconfont icon-zanting"></span>
    <!-- 控制区域背景 -->
    <div class="custom-video_control">
        <!-- 视频进度条 -->
        <div class="custom-video_control-bg">
          <div 
            class="custom-video_control-bg-outside"
            ref="custom-video_control-bg-outside"
          >
            <span 
              class="custom-video_control-bg-inside"
              ref="custom-video_control-bg-inside"
            ></span>
            <span 
              class="custom-video_control-bg-inside-point"
              ref="custom-video_control-bg-inside-point"
            ></span>
          </div>
        </div>
        <!-- 声音 -->
        <div class="custom-video_control-voice">
          <span class="custom-video_control-voice-play iconfont icon-shengyin"></span>
          <div class="custom-video_control-voice-bg">
            <div class="custom-video_control-voice-bg-outside">
              <span class="custom-video_control-voice-bg-inside"></span>
              <span class="custom-video_control-voice-bg-point"></span>
            </div>
          </div>
        </div>
        <!-- 时间 -->
        <div class="custom-video_control-time">
          <span>"00:00"</span>/<span>"00:00"</span>
        </div>
        <!-- 全屏缩放 -->
        <span class="custom-video_control-full iconfont icon-quanping"></span>
    </div>
  </div>

Соответствующий css и полный код будут прикреплены в конце статьи. Следующая связанная логика будет выполнять привязку и расширение событий на основе приведенной выше структуры DOM.

2. Воспроизведение и пауза

Существует два сценария воспроизведения или паузы: первый — нажать кнопку «Воспроизвести» или «Пауза» для управления воспроизведением или паузой, второй — щелкнуть область видео для управления воспроизведением или паузой.
Первая реализация заключается в нажатии кнопки «Воспроизвести», стиль этой кнопки становится «Пауза», а приведенная выше область комментариев изменяется.кнопка воспроизведения или паузыэлемент

     <!--播放或者暂停按钮-->
    <span
      v-if="videoState.play"
      class="custom-video_play custom-video_play-pause iconfont icon-zanting"
      @click="pause('btn')"
    >
    </span>
    <span
      v-else
      class="custom-video_play custom-video_play-play iconfont icon-bofang"
      @click="play('btn')"
    >
    </span>

И соответствующий js выглядит следующим образом:

data() {
    return {
      videoState: {
        play: false, //播放状态
        playState: false, // 记录播放状态
      },
      videoDom: null, // video
    }
},
mounted() {
    // 初始化相关元数据
    this.videoDom = this.$refs["custom-video"]
},
methods: {
    play(flag) { // 播放按钮事件
      if(flag) this.videoState.playState = true
      this.videoState.play = true
      this.videoDom.play()
    },
    pause(flag) { // 暂停按钮事件
      if(flag) this.videoState.playState = false
      this.videoDom.pause()
      this.videoState.play = false
    },
}

Нажмите кнопку «воспроизведение», чтобы вызвать метод play (). Изменение значения videoState.play приводит к тому, что стиль кнопки становится «приостановленным», и вызывается метод воспроизведения, который поставляется с тегом видео, в противном случае он «приостанавливается». ".
Второй способ нажать на экран для воспроизведения или паузы — это прослушать событие клика по видео. Код выглядит следующим образом:

mounted() {
    // 初始化相关元数据
    this.videoDom = this.$refs["custom-video"]
    this.initMedaData()
},
methods: {
    initMedaData() { // 初始化video相关事件
      this.videoDom.addEventListener("click", () => { // 点击视频区域可以进行播放或者暂停
        if(this.videoDom.paused || this.videoDom.ended) {
            if(this.videoDom.ended) {
              // 如果视频结束,currentTime初始化为0
              this.videoDom.currentTime = 0
            }
            this.play('btn') //调用下面play的方法
        } else {
          this.pause('btn') //调用下面pause的方法
        }
      })
    },
    play(flag) { // 播放按钮事件
      if(flag) this.videoState.playState = true
      this.videoState.play = true
      this.videoDom.play()
    },
    pause(flag) { // 暂停按钮事件
      if(flag) this.videoState.playState = false
      this.videoDom.pause()
      this.videoState.play = false
    },
}

3. Отображение времени

внизу справа на картинке"00:00 / 00:29", это текущее время воспроизведения больше, чем общая продолжительность предыдущего видео, видео имеет свои собственные атрибуты currentTime и duration, единица измерения, полученная этими двумя полями, равна s, поэтому для выполнения преобразования формата сначала измените соответствующую структуру dom. :

<!-- 时间 -->
<div class="custom-video_control-time">
  <span>{{currentTime ? currentTime : "00:00"}}</span>
   / 
  <span>{{duration ? duration : "00:00"}}</span>
</div>

Соответствующий код js выглядит следующим образом:

data() {
    return {
      duration: 0, // 视频总时长
      currentTime: 0, // 视频当前播放时长
    }
},
mounted() {
    // 初始化相关元数据
    this.videoDom = this.$refs["custom-video"]
    this.initMedaData()
},
methods: {
    initMedaData() { // 初始化video相关事件
      this.videoDom.addEventListener('loadedmetadata', () => { // 获取视频总时长
        this.duration = this.timeTranslate(this.videoDom.duration)
      })
    },
    this.videoDom.addEventListener("timeupdate", () => { // 监听视频播放过程中的时间
        this.currentTime = this.timeTranslate(this.videoDom.currentTime)
    }),
    timeTranslate(t) { // 时间转化
      let m = Math.floor(t / 60)
      m < 10 && (m = '0' + m)
      return m + ":" + (t % 60 / 100 ).toFixed(2).slice(-2)
    },
}

4. Отображение прогресса воспроизведения

  1. Ход воспроизведения постепенно удлиняется по мере воспроизведения, а индикатор выполнения воспроизведения состоит из трех частей:

Метод timeupdate, поставляемый с видео, будет отслеживать состояние воспроизведения в режиме реального времени и получать соотношение currentTime и duration в реальном времени.Это значение отношения представляет собой пропорцию внутреннего пространства ко всему внешнему на рисунке. код показывает, как показано ниже:

data() {
    return {
      videoDom: null, // video
      videoProOut: null, // 视频总进度条
      videoPro: null, // 视频进度条
      videoPoi: null, // 视频进度点
    }
},
mounted() {
    // 初始化相关元数据
    this.videoDom = this.$refs["custom-video"]
    this.videoProOut = this.$refs['custom-video_control-bg-outside']
    this.videoPro = this.$refs['custom-video_control-bg-inside']
    this.videoPoi = this.$refs['custom-video_control-bg-inside-point']
    this.initMedaData()
},
methods: {
    initMedaData() { // 初始化video相关事件
      this.videoDom.addEventListener('loadedmetadata', () => { // 获取视频总时长
        this.duration = this.timeTranslate(this.videoDom.duration)
      })
    },
    this.videoDom.addEventListener("timeupdate", () => { // 监听视频播放过程中的时间
        const percentage = 100 * this.videoDom.currentTime / this.videoDom.duration
        // 页面渲染进度
        this.videoPro.style.width = percentage + '%'
        this.videoPoi.style.left = percentage - 1 + '%'
    })
}
  1. Ход воспроизведения можно щелкнуть и перетащить. В этих двух шагах используются события mousedown, mousemove и mouseup, соответствующие трем методам handlePrograssDown, handlePrograssMove и handlePrograssUp. Эти три метода подключаются к узлу dom следующим образом:
<!-- 进度条 -->
<div
  class="custom-video_control-bg"
  @mousedown="handlePrograssDown"
  @mousemove="handlePrograssMove"
  @mouseup="handlePrograssUp"
>
  <!--此处省略视频进度条dom结构-->
</div>

Перетащите или щелкните индикатор выполнения, чтобы сначала вычислить горизонтальное расстояние начальной точки индикатора выполнения, и необходимо рассчитать смещение этой точки.Метод инкапсуляции смещения getOffset выглядит следующим образом (ps: выглядит как фрагмент исходного кода zepto ),

getOffset(node, offset) { // 获取当前屏幕下进度条的左偏移量和上偏移量
  if(!offset) {
    offset = {}
    offset.left = 0
    offset.top = 0
  }
  if(node === document.body || node === null) {
    return offset
  }
  offset.top += node.offsetTop
  offset.left += node.offsetLeft
  return this.getOffset(node.offsetParent, offset)
},

Логика клика следующая:

handlePrograssDown(ev) { // 监听点击进度条事件,方便获取初始点击的位置
  this.videoState.downState = true //按下鼠标标志
  this.pause() // 视频暂时停止
  this.videoState.distance = ev.clientX - this.videoState.leftInit //记录点击的离起点的距离
  这里的leftInit就是通过getOffset方法获取的进度条起点偏移量
},

Отпустите кнопку мыши, рассчитайте текущее текущее время по записанному расстоянию, а затем воспроизведите или приостановите видео с этой точки, логика следующая:

handlePrograssUp() { //松开鼠标,播放当前进度条视频
  this.videoState.downState = false
  // 计算点击此处的currentTime
  this.videoDom.currentTime = this.videoState.distance / this.processWidth * this.videoDom.duration
  // 页面回显的currentTime数据
  this.currentTime = this.timeTranslate(this.videoDom.currentTime)
  // 这个是判断当前视频是在播放状态进行点击还是在暂停状态进行点击的
  if(this.videoState.playState) {
    this.play()
  }
},

Состояние this.videoState.playState выше определяется нажатием кнопки или области видео, в частности, в методах play(flag) и pause(flag) выше.

  1. Метод перетаскивания выглядит следующим образом:
handlePrograssMove(ev) { // 监听移动进度条事件,同步播放相关事件
  if(!this.videoState.downState) return //如果没有通过鼠标点击起点,则直接不进行下面计算
  let disX = ev.clientX - this.videoState.leftInit
  // 进行边界判断
  if(disX > this.processWidth) {
    disX = this.processWidth
  }
  if(disX < 0) {
    disX = 0
  }
  this.videoState.distance = disX
  // 计算当前的currentTime
  this.videoDom.currentTime = this.videoState.distance / this.processWidth * this.videoDom.duration
},

В центре внимания индикатора выполнения воспроизведения находится вычисление момента времени текущего видео, щелкнув или перетащив позицию, и назначив это значение тегу видео, вот

this.videodom.currentTime = выражение

Этот метод timeupdate будет активирован:

this.videoDom.addEventListener("timeupdate", () => { // 监听视频播放过程中的时间
    const percentage = 100 * this.videoDom.currentTime / this.videoDom.duration
    // inside进度条长度
    this.videoPro.style.width = percentage + '%'
    // point移动变化
    this.videoPoi.style.left = percentage - 1 + '%'
    this.currentTime = this.timeTranslate(this.videoDom.currentTime)
})

5. Управление звуком

Индикатор выполнения звука и видео аналогичен, за исключением того, что индикатор выполнения звука рассчитывается в вертикальном направлении.Свойство громкости, связанное со звуком, имеет диапазон значений от 0 до 1. Метод мониторинга звука — изменение громкости. Стиль звука Стиль изображения следующий:

Соответствующие методы следующие:

// 监听声音的方法,通过此方法进行进度条渲染
this.videoDom.addEventListener("volumechange", () => {
    const percentage =  this.videoDom.volume * 100
    this.voicePro.style.height = percentage + '%'
    this.voicePoi.style.bottom = percentage + '%'
})

// 声音控制的三个方法
handleVolPrograssDown(ev) { // 监听声音点击事件
  this.voiceState.topInit = this.getOffset(this.voiceProOut).top
  this.volProcessHeight = this.voiceProOut.clientHeight
  this.voiceState.downState = true //按下鼠标标志
  this.voiceState.distance = ev.clientY - this.voiceState.topInit
},
handleVolPrograssMove(ev) { // 监听声音进度条移动事件
  if(!this.voiceState.downState) return
  let disY = this.voiceState.topInit + this.volProcessHeight - ev.clientY
  if(disY > this.volProcessHeight - 2) {
    disY = this.volProcessHeight - 2
  }
  if(disY < 0) {
    disY = 0
  }
  this.voiceState.distance = disY
  this.videoDom.volume = this.voiceState.distance / this.volProcessHeight
  this.videoOption.volume = Math.round(this.videoDom.volume * 100)
},
handleVolPrograssUp() { // 监听声音鼠标离开事件
  this.voiceState.downState = false //按下鼠标标志
  this.videoDom.volume = this.voiceState.distance / this.volProcessHeight
  this.videoOption.volume = Math.round(this.videoDom.volume * 100)
},

6. Полноэкранный режим и выход из полноэкранного управления

Полноэкранный метод, совместимый с обработкой:

fullScreen() {
  let ele = document.documentElement
  if (ele .requestFullscreen) {
    ele .requestFullscreen()
  } else if (ele .mozRequestFullScreen) {
    ele .mozRequestFullScreen()
  } else if (ele .webkitRequestFullScreen) {
    ele .webkitRequestFullScreen()
  }
  // 对应的video标签大小100%
  this.$refs['custom-video_container'].style.width = "100%"
  this.$refs['custom-video_container'].style.height = "100%"
},

Выход из полноэкранного режима:

exitFullscreen() {
  let de = document
  if (de.exitFullscreen) {
    de.exitFullscreen();
  } else if (de.mozCancelFullScreen) {
    de.mozCancelFullScreen();
  } else if (de.webkitCancelFullScreen) {
    de.webkitCancelFullScreen();
  }
  // 返回初始化值
  this.$refs['custom-video_container'].style.width = "500px"
  this.$refs['custom-video_container'].style.height = "300px"
}

Примечание. Индикатор выполнения и события выхода из полноэкранного режима здесь не обрабатывают клавиши клавиатуры, а просто события щелчка мышью.

7. Скрыть и отобразить панель управления

Панель управления отображается, когда она поставлена ​​на паузу. Во время воспроизведения, пока мышь находится в видеоплеере, она также будет отображаться. Она исчезнет через несколько секунд после выхода. Здесь используется анимация перехода vue:

<div
    class="custom-video_container"
    ref="custom-video_container"
    @mouseover="handleControls($event, 'start')"
    @mouseleave="handleControls($event, 'end')"
>
    <!--省略-->
    <transition>
        name="fade"
    >
        <div
            class="custom-video_control"
            v-show="!videoState.hideControl || !videoState.play"
        >
        <!--控制栏dom元素-->
        </div>
    </transition>
</div>

Соответствующий файл css

/* 控制栏隐藏动画 */
.fade-enter-active {
  transition: all .3s ease;
}
.fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.fade-enter, .fade-leave-to {
  transform: translateY(50px);
  opacity: 0;
}

Исчезновение или отображение панели управления логически контролируется привязкой событий mouseover и mouseleave самого внешнего элемента dom. Здесь вместо mouseout используется Mouseleave, если используется событие mouseout, то при переходе по панели управления будет мерцание. Исходный код конкретного события выглядит следующим образом:

handleControls(ev, flag) { // 监听离开或者进入视频区域隐藏或者展示控制栏
  switch (flag) {
    case 'start':
      this.videoState.hideControl = false
      break;
    case 'end':
      this.videoState.hideControl = true
      break;
    default:
      break;
  }
},

Показать или скрыть панель управления, управляя состоянием this.videoState.hideControl.

В-четвертых, исходный код

Адрес источника:vue-player