This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

“Welcome to the discussion in the comments section. The excavation authorities will draw 100 nuggets in the comments section after project Diggnation. See the event article for details.”

Video Basics

First we create a video tag and introduce the video, label it with the ref attribute and name it ‘video’ for easy access and call.

Set properties:

  • src: Imports video sourcessrc
  • poster: Preloads images
  • controls:falseTo mask browser native controls
  • autoplay:trueTo enable the auto play function
<template>
  <div class="video">
    <video
      ref="video"
      class="video__player pointer"
      :src="src"
      :poster="poster"
      :controls="false"
      :autoplay="true"
    />
  </div>
</template>

Copy the code

Set the controls

After canceling the native controls, we need to make corresponding controls by ourselves, such as: progress bar, Play/pause, volume, play duration, etc. :

<template>
  ...
  
  <div class="video__controller bg-primary" :class="{ hide: currentTime > 2 }">
    
    <div
      v-if="paused"
      class="play video__controllerItem pointer"
      @click="switchPaused"
    >
      <img class="icon" src="/icon/play_white.png" alt="play" />
    </div>
    <div
      v-else
      class="pause video__controllerItem pointer"
      @click="switchPaused"
    >
      <img class="icon" src="/icon/pause_white.png" alt="pause" />
    </div>
    
    <div class="time video__controllerItem">
      {{timeStamp(currentTime)}} / {{timeStamp(duration)}}
    </div>
    
    <div class="volume video__controllerItem">
      <img class="icon" src="/icon/volume_white.png" alt="volume" />
    </div>
    
    <img
      v-if="paused"
      class="statusIcon pointer"
      src="/icon/play_white.png"
      alt="statusIcon"
      @click="switchPaused"
    />
  </div>
  ...
</template>

Copy the code

Setting video Styles

For the video element, its raw size is the size of the video it is playing. The visual image of a video is always scaled to the size of the video it is playing. This is similar to setting the background image of an element to background-size: Cover, so if the video element is set to width:100% and height:100%, it will not be stretched as expected, but will behave in its original proportions. Whether to set width:100%, height:100%

Width :100%, height:100%

Width :100%, height:100%

At this point, a prototype video appeared.


Play/pause state toggles

Video Object method:

  • canPlayType(): Checks whether the browser can play the specified video type
  • load()Reload the video element
  • play(): Starts to play the video
  • pause(): Pauses the currently playing video

We need to give a state value paused, which represents the paused state of the current video. According to this value, the video playback state can be switched.

Since we set autoplay=”true”, autoplay is played by default. So paused should have the initial value false.

<template>
  <div class="video">
    <video ref="video" @click="switchPause" />
  </div>
</template>

Copy the code
<script> export default { data() { return { paused: false }; }, methods: { switchPause() { if (this.paused) { this.$refs.video.play(); this.paused = false; } else { this.$refs.video.pause(); this.paused = true; }}}}; </script>Copy the code

Current time/video duration calculation & display

The playback duration is presented in a simple format: current time/total video duration

So how do you get the current playing time and total video length?

We need to use the properties of video itself, Duration and currentTime, as well as the two bound events of video, timeUpdate (when the browser has loaded the video’s metadata), Loadedmetadata (when the current playing position has changed), As the event trigger of “update video playing time” and “Get total video duration” to obtain the current video playing time and total video duration:

<video
  ref="video"
  class="video__player pointer"
  :src="src"
  :poster="poster"
  :controls="false"
  :autoplay="focus"
  @click="switchPaused"
  @loadedmetadata="loadedmetadata"
  @timeupdate="timeupdate"
/>

Copy the code
data() {
  return {
    ...
    duration: 0,
    currentTime: 0
    ...
  }
},
methods: {
  ...
  
  loadedmetadata() {
    this.duration = this.$refs.video.duration
  },
  
  timeupdate() {
    this.currentTime = this.$refs.video.currentTime
  }
  ...
}

Copy the code

At this point we find that the video duration is in ‘seconds’ and needs to be converted

So we need to construct a utility function to convert the number of seconds to hours: minutes: seconds:


timeStamp(t) {
  
  function f(num) {
    return num < 10 ? '0' + num : num
  }
  const s = parseInt(t) % 60
  let m = parseInt(t / 60)
  let time = `${f(m)}:${f(s)}`

  if (m > 60) {
    m = parseInt(t / 60) % 60
    const h = parseInt(parseInt(t / 60) / 60)
    time = `${f(h)}:${f(m)}:${f(s)}`
  }
  return time
}

Copy the code

This will normally display the current time/video length.


Progress bar & Volume

The progress bar

For the progress bar, we need two areas

  • Total video Durationvideo__duration(Light white)
  • The video has been playedvideo__currentTime(yellow)

If it is a network video, the cache progress buffered should be displayed

And a sign, to show the play position to jump, and the corresponding time is displayed above

Here to further improve, there should be a corresponding frame of the video thumbnail

And calculate the ratio of currentTime to total Duration on timeUpdate event, named progressVal. Using Transform: scaleX(x) and the calculated progressVal, scale the length of the played progress bar to show the progress.

Next, we listen for the mouse mousemove event on the progress bar Video__track to dynamically change where the logo sign will be played.

Finally, when we click where sign is, we change the playing position of the video by modifying the currentTime property

Details are as follows:

<div
  class="video__track pointer"
  :class="{ hide: currentTime > 2 }"
  @mousemove="moveSign"
  @click="progressChange"
>
  <div class="video__duration">
    <div
      class="video__currentTime"
      :style="{ transform: `scaleX(${progressVal})` }"
    />
  </div>
  <div ref="sign" class="video__sign pointer">
    <div class="video__signTime">{{timeStamp(signTime)}}</div>
  </div>
</div>

Copy the code
data() { return { ... progressVal: 0, signTime: 0 ... } }, methods: { ... timeupdate() { const { duration, currentTime } = this.$refs.video this.currentTime = currentTime this.progressVal = currentTime / duration }, moveSign(e) { const mouseX = e.clientX const signWidth = this.$refs.sign.getBoundingClientRect().width const videoWidth = this.$refs.video.getBoundingClientRect().width const videoLeft = this.$refs.video.getBoundingClientRect().left const X  = mouseX - videoLeft - signWidth / 2 this.$refs.sign.style.left = `${X}px` this.signTime = this.$refs.video.duration * (X / videoWidth) }, progressChange() { this.$refs.video.currentTime = this.signTime } ... }Copy the code

Of course, if you want to make the progress bar look better, you can also add some interesting elements at the end of the progress bar, such as:

Drag and drop

Dragging is essentially a process of pressing down (Mousedown) → moving (mousemove) → releasing (Mouseup) :

  • After pressed: Pause video playback and start monitoring movementmousemoveEvent until release ends the drag operation.
  • Dragging: Change the length of the progress bar and dynamically change the video playback timecurrentTimeDisplay.
  • After release: end the drag event, the video will jump to the corresponding position to start playing.

To optimize the experience, we bind Mousemove to the Document document object. This way, even if the mouse goes over the video area, it won’t interrupt the dragging of the progress bar.

Due to moving over a document object, the mouse position exceeds the allowable range. So we need to limit the displacement result 0 <= X <= videoWidth.

<div
  class="video__track pointer"
  :class="{ hide: currentTime > 2 }"
  @mousemove="moveSign"
  @click="progressChange"
  @mousedown="dragProgress"
>
  ...
</div>

Copy the code
dragProgress() { this.$refs.video.pause() const progressMove = e => { const mouseX = e.clientX const signWidth = this.$refs.sign.getBoundingClientRect().width const videoWidth = this.$refs.video.getBoundingClientRect().width const videoLeft = this.$refs.video.getBoundingClientRect().left let X = mouseX - videoLeft - signWidth / 2 if (X <= 0) { X = 0  } else if (X >= videoWidth) { X = videoWidth } this.progressVal = X / videoWidth this.currentTime = this.$refs.video.duration * (X / videoWidth) } document.addEventListener('mousemove', progressMove) document.addEventListener('mouseup', e => { this.$refs.video.currentTime = this.currentTime this.$refs.video.play() this.paused = false document.removeEventListener('mousemove', progressMove) }) }Copy the code

Note: If you need to make custom scrollbars in your project, you can also modify the above “drag and drop” code and apply it.

The volume

The volume adjustment is similar to dragging the progress bar, but instead of currentTime, the volume object is changed

Note also that the volume ranges from 0.0 to 1.0 (the default is 1.0)


Multiple rate playback

By changing the properties of defaultPlaybackRate and playbackRate of video, we can adjust the playbackRate.

The magnification rates are generally 0.5, 1.0, 1.25, 1.5, 2.0. 1.0 indicates the normal playback speed.

  • defaultPlaybackRateSets or returns the default playback speed of audio/video
  • playbackRateSets or returns the speed of audio/video playback

Generally, the “double speed” option will be added in the lower right corner of the player for users to adjust


Other things to watch out for

  • What if the video fails to play?

The play() of the video object is actually a Promise function.

The play() method is followed by.catch(err => {}). For example, a message indicating that a video cannot be played is displayed.

const Play = this.media.play().catch(err => {
  
});

Copy the code

The above is a video player solution based on local resources, if it is in the form of network load streaming media, there is a lot to be studied.

This will be explained in later chapters.

Reference:

  1. HTML <video>Tags:www.w3school.com.cn/tags/tag_vi…
  2. The HTML DOM Video object: www.w3school.com.cn/jsref/dom_o…
  3. HTML 5 video/audio reference manual: www.w3school.com.cn/tags/html_r…
  4. Write a Web video player from scratch: juejin. Im /entry/ 5a5C1…

Full screen zoom

Full screen Element. RequestFullscreen () is equivalent to press F11

requestFullscreen() {
  if (this.$refs.video.requestFullscreen) {
    this.$refs.video.requestFullscreen()
  } else if (this.$refs.video.msRequestFullscreen) {
    this.$refs.video.msRequestFullscreen()
  } else if (this.$refs.video.mozRequestFullScreen) {
    this.$refs.video.mozRequestFullScreen()
  } else if (this.$refs.video.webkitRequestFullscreen) {
    this.$refs.video.webkitRequestFullscreen()
  }
  this.fullscreen = true
}

Copy the code

Canceling full screen document.exitFullscreen () is equivalent to full screen after pressing Esc

cancelFullscreen() {
  if (document.exitFullscreen) {
    document.exitFullscreen()
  } else if (document.msExitFullscreen) {
    document.msExitFullscreen()
  } else if (document.mozCancelFullScreen) {
    document.mozCancelFullScreen()
  } else if (document.oRequestFullscreen) {
    document.oCancelFullScreen()
  } else if (document.webkitExitFullscreen) {
    document.webkitExitFullscreen()
  }
  this.fullscreen = false
}

Copy the code

The important thing to note here is that achieving full screen can only be triggered manually by the user, that is, by events, not directly in code. Therefore, it is not possible to enter the page immediately full screen.