Vue song progress bar sample code

Vue song progress bar sample code

Note that this is not a project created by vue-cli. It is an HTML file written by referencing vue.js. You can use it by simply pasting it into an HTML file. My music link will become invalid after a period of time. You need to prepare the music yourself. There are functions for dragging and clicking to switch the playback progress.

demo pictures

insert image description here

Code

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <audio ref="audioRef" autoplay @canplay='canplay' @timeupdate='update'></audio>
    <button @click="play">Play</button>
    <button @click="pause">Pause</button>
    <div class="progress-wrapper">
      <span class="time time-l">{{formatTime(currentTime)}}</span>
      <div class="progress-bar-wrapper">
        <cpn :progress=progress 
          @progress-changing="onProgressChanging" 
          @progress-changed='progressChanged'>
       </cpn>
      </div>
      <span class="time time-l">{{formatTime(duration)}}</span>
    </div>

  </div>

  <!-- Child Component -->
  <template id="myCpn">

    <div class="progress-bar">
      <!-- Black strip at the back-->
      <div class="bar-inner" @click="clickProgress">
        <!-- Areas that have been played-->
        <div class="progress" :style='progressStyle' ref="progress">
        </div>
        <!-- btn -->
        <div class="progress-btn-wrapper" :style='btnStyle' @touchstart.preventDefault='onTouchStart'
          @touchmove.preventDefault='onTouchMove' @touchend.preventDefault='onTouchEnd' >
          <div class="progress-btn"></div>
        </div>
      </div>
    </div>
  </template>


  <script src="../../js/vue.js"></script>

  <script>
    audioEl = null
    const progressBtnWidth = 16
    // Subcomponent const cpn = {
      template: "#myCpn",
      props: {
        progress:
          type: Number,
          default: 0
        }
      },
      data() {
        return {
          offset: 0
        }
      },
      mounted() {

      },
      created() {
        this.touch = {}
      },
      computed: {
        progressStyle() {
          return `width: ${this.offset}px`
        },
        btnStyle() {
          // console.log('fds');
          return `transform: translate3d(${this.offset}px,0,0)`
        },
      },
      watch:
        progress(newProgress) {
          // Progress bar width const barWidth = this.$el.clientWidth - progressBtnWidth
          this.offset = barWidth * newProgress
        }
      },
      methods: {
        onTouchStart(e) {
          // console.log(e);
          this.touch.x1 = e.changedTouches[0].clientX
          // Yellow progress bar initial width this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchMove(e) {
          // console.log(e);
          // x offset const delta = e.changedTouches[0].clientX - this.touch.x1
          // Previous width + the offset added by dragging this time = the expected length of the yellow bar const tempWidth = this.touch.beginWidth + delta
          // Get barWidth again
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // Yellow bar length/barwidth = progress The current progress should be const progress = tempWidth / barWidth
          this.offset = barWidth * progress
          this.$emit('progress-changing', progress)
          // console.log("tempWidth", tempWidth);
          // console.log("barWidth", barWidth);
          // console.log("progress", progress);

        },
        onTouchEnd(e) {
          // console.log(e);
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const progress = this.$refs.progress.clientWidth / barWidth
          this.$emit('progress-changed', progress)
        },
        // Click the progress bar clickProgress(e){
          // console.log("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          const rect = this.$el.getBoundingClientRect()
          // The width of the yellow bar const offsetWidth = e.pageX - rect.x
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const progress = offsetWidth / barWidth
          this.$emit('progress-changed', progress)
          console.log(offsetWidth)
        }
      },
    }

    const app = new Vue({
      el: "#app",
      data: {
        content: 'fdasdf',
        src: 'https://music.163.com/song/media/outer/url?id=1463165983.mp3',
        currentTime: 0,
        duration: 0,
        isplay: false,
        progressChanging : false
      },
      components:
        cpn
      },

      mounted() {
        this.$nextTick(() => {
          audioEl = this.$refs.audioRef
          audioEl.src = this.src
          // Pause by default audioEl.pause()
        })
      },
      computed: {
        progress() {
          return this.currentTime / this.duration
          console.log("progress", this.currentTime / this.duration);
        },

      },
      methods: {
        play() {
          audioEl.play()
          this.isplay = true
        },
        pause() {
          audioEl.pause()
          this.isplay = false
          // console.log();
        },
        canplay(e) {
          // console.log(123456);
          console.log(e);
          this.duration = e.target.duration
        },
        update(e) {
          if(!this.progressChanging){
            this.currentTime = e.target.currentTime
          }
        },
        onProgressChanging(e) {
          // console.log("onProgressChanging", e);
          this.progressChanging = true
          // Modify the currentTime value in real time this.currentTime = this.duration * e 
        },
        progressChanged(e){
          // console.log(e);
          this.progressChanging = false
          audioEl.currentTime = this.currentTime = this.duration * e 
          if(!this.isplay){
            console.log("------");
            audioEl.play()
          }
        },
        formatTime(interval) {
          // interval rounded down interval = interval | 0
          // If there are less than two digits, fill it with a 0
          let minute = ((interval / 60 | 0) + '')
          let second = ((interval % 60 | 0) + '')
          let len ​​= minute.length
          for (; len < 2; len++) {
            minute = '0' + minute
          }
          len = second.length
          for (; len < 2; len++) {
            second = '0' + second
          }
          return `${minute}:${second}`
        },

      },
    })
  </script>
</body>
<style>
  #app {
    width: 100%;
  }

  .progress-wrapper {
    display: flex;
    width: 80%;
    padding: 10px 0;
    align-items: center;
    margin: 0 auto;
  }

  .time {
    width: 40px;
    flex: 0 0 40px;
    font-size: 8px;
    margin: 0 auto;
    padding: 0 8px;
  }

  .time-l {
    text-align: left;
  }

  .time-l {
    text-align: right;
  }

  .progress-bar-wrapper {
    flex: 1;
  }

  /* Subcomponent style */
  .progress-bar {
    height: 30px;
  }

  .bar-inner {
    position: relative;
    top: 11px;
    height: 8px;
    background-color: rgba(87, 82, 82, 0.062);
    border-radius: 5px;
  }

  .progress {
    position: absolute;
    height: 100%;
    background-color: rgb(238, 238, 136);
  }

  .progress-btn-wrapper {
    position: absolute;
    left: -8px;
    top: -11px;
    width: 30px;
    height: 30px;
  }

  .progress-btn {
    position: relative;
    top: 7px;
    left: 7px;
    box-sizing: border-box;
    width: 16px;
    height: 16px;
    border: 3px solid rgb(189, 189, 218);
    border-radius: 50%;
    background: rgb(123, 192, 212);
  }
</style>

</html>

Commentary

https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent

The progress bar in the middle is a progress bar component. The black background is the total length of the progress. The yellow bar on the left is the current playback progress. The slider in the middle can be dragged left and right to manually change the progress bar. During playback, the progress bar will become longer and the slider will shift to the right. You can drag the slider left and right to change the playback progress and the time on the left will change.

To achieve the playback process, the progress bar will also play. What determines the status of the component? It can be determined by the progress. Any state of the component can be determined based on the progress. The parent component passes a digital type of progress

The position of the btn and the width of the progress yellow bar are calculated based on the progress. The width can be represented by a data offset (define a data). Then, we need to monitor the progress.

https://cn.vuejs.org/v2/api/#vm-el

Knowledge to get the root DOM element

      watch:
        progress(newProgress) {
          // Progress bar width const barWidth = this.$el.clientWidth - progressBtnWidth
          //Offset this.offset = barWidth * newProgress
        }
      }

Of course, you can use computed, but you should note that you cannot get the width of el at the beginning. Computed calculates once at the beginning, accesses offset when the template is rendered, and then calculates the width of el. At this time, the component has not been mounted and cannot be obtained. If you use watch, it has actually been rendered when progress changes, so clientWidth can be obtained. In addition, because some logic needs to be processed later, it is more inclined to logic writing, so watch should be used to implement it.

After we have the offset, we need to map the DOM and set a dynamic style for the yellow progress bar and btn.

insert image description here

Both of their styles are calculated based on offset.

      computed: {
        progressStyle(){
          return `width: ${this.offset}px`
        },
        btnStyle() {
          return `transform: translate3d(${this.offset}px,0,0)`
        }
      },

Now let's calculate its style based on the offset. We accept the progress attribute. When the external progress changes, its offset is calculated based on the progress. With the offset, the style can change.

Question: flex 0 0 40px and width have similar effects, but in some cases, the flex layout will be squeezed or collapsed, resulting in the width being squeezed, so setting width can ensure that our width does not change

Here is the canplay event monitor

insert image description here

Parent component calculates the property playback progress: playback time/total time. The total time has been obtained. The playback time can be monitored with an event: timeupdate

insert image description here

Current effect

It can be seen that this is seconds, and the time needs to be formatted. Define a tool function

IIFE: Self-executing function currying and some bitwise operations https://www.jianshu.com/p/a3202bc3f7a4

insert image description here

insert image description here

A question: Why is xxx.yyy|0 equal to xxx? Why can the or operator here have the function of rounding?

Knowledge Padstart Method

formatTime Function

formatTime(interval) {
  // interval rounded down interval = interval | 0
  // If there are less than two digits, fill it with a 0
  const minute = ((interval / 60 | 0) + '').padstart(2, '0')
  const second = ((interval % 60 | 0) + '').padstart(2, '0')
  return `${minute}:${second}`
}

But it can't recognize the padstart method.

So I just wrote it myself

        formatTime(interval) {
          // interval rounded down interval = interval | 0
          // If there are less than two digits, fill it with a 0
          let minute = ((interval / 60 | 0) + '')
          let second = ((interval % 60 | 0) + '')
          let len ​​= minute.length
          for(;len<2;len++){
            minute='0'+minute
          }
          len = second.length
          for(;len<2;len++){
            second='0'+second
          }
          return `${minute}:${second}`
        }

Next, write the interactive logic of the progress bar

Support drag and click

The most common ones on mobile devices are ontouchstart ontouchmove ontouchend
https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent

Knowledge prevent modifier

Add three events to the slider

      methods: {
        onTouchStart(e) {
          console.log(e);
        },
        onTouchMove(e) {
          console.log(e);
        },
        onTouchEnd(e) {
          console.log(e);
        }
      },

Two pieces of information need to be obtained. One is to know the location where it was clicked, that is, to know what its horizontal coordinate is. And the width of the progress bar on the left (offset)

[screenX clientX pageX concept

Because the position of the horizontal axis also needs to be obtained during touchmove, the data can be bound to a shared object. An object can be defined in the created hook function.

      created() {
        this.touch = {}
      },

After giving the yellow bar a ref

        onTouchStart(e) {
          // console.log(e);
          this.touch.x1=e.changedTouches[0].clientX
          // Yellow progress bar initial width this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchStart(e) {
          // console.log(e);
          this.touch.x1=e.changedTouches[0].clientX
          // Yellow progress bar initial width this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchMove(e) {
          // console.log(e);
          // x offset const delta = e.changedTouches[0].clientX-this.touch.x1
          // Previous width + the offset added by dragging this time = the expected length of the yellow bar const tempWidth = this.touch.beginWidth + delta
          // Get barWidth again
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // Yellow bar length/barwidth = progress The current progress const progress = tempWidth/barWidth
          this.offset = barWidth * progress
          // console.log("tempWidth", tempWidth);
          // console.log("barWidth", barWidth);
          // console.log("progress", progress);

        },

Let's sort it out. The ultimate goal is to get the offset. The offset is determined by progress and barWidth. How to calculate progress here? You need to get the current width of the yellow bar divided by the total width. The width of the yellow bar is the initial width + the x distance of this slide. Then it is simple to get barWidth, and then you can calculate it.

Do you think this is redundant? Can't we just use the original yellow bar width + the length of this slide? Why do we need to calculate progress? Because we need to let the outside world know that the progress of the song has changed, and we need to make them correspond. Ultimately, we need to modify the audio. This is done with the parent component. Now we only implement dragging, so we need to dispatch events. Here we dispatch two custom events, one progress-changing event, which means that the finger is still in the process of dragging and has not left. When the finger leaves, a progress-change event is dispatched to pass the new progress.

Modify the value of currentTime in real time

insert image description here

This is to modify the currentTIme when dragging, and the time to modify the music is when the hand is released.

insert image description here

However, we found that it can be dragged when it is paused, but there is a problem when dragging during playback.

Optimization: When changing, if the effect is paused, let it play. At this time, you need to define an isplay that flips when clicking play pause.

insert image description here

Now let's fix the bug. When playing, dragging the progress will cause problems. Why? By monitoring progressChanging, we modify the currentTime. Once the currentTime changes, progress will make a new calculation based on the currentTime and then pass it to the child component. The child component will enter this logic.

insert image description here

The offset will be recalculated.

Finally, this will cover

insert image description here

Some control should be done during the update, and a flag should be added during the changing process.

insert image description here

That is to say, in the update function, if changing is in the process of dragging, do not modify currentTime. In the process of changing, it is considered that the progress bar is changed, and the priority of modifying the progress bar is high. The priority of currentTime change caused by its own playback is relatively low.

That's it.

In addition to dragging, we also want to click it to jump to the corresponding position.

Knowledge webapi --getBoundingClientRect method returns the size of the element and its position relative to the viewport (get the shorter one).

insert image description here

Use pagex to get the long one

        clickProgress(e){
          // console.log("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          const rect = this.$el.getBoundingClientRect()
          // The width of the yellow bar const offsetWidth = e.pageX - rect.x
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const progress = offsetWidth / barWidth
          this.$emit('progress-changed', progress)
          console.log(offsetWidth)
        }

This is the end of this article about vue song progress bar demo. For more relevant vue song progress bar content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Vue2.0+SVG realizes the circular progress bar component of music playback
  • Vue2.0 implements music/video playback progress bar component
  • vue.js+ElementUI realizes the effect of progress bar prompting password strength
  • Progress bar function when vue page is loading (example code)

<<:  MySQL 8.0.22 zip compressed package version (free installation) download, installation and configuration steps detailed

>>:  HTML Tutorial: Unordered List

Recommend

Detailed Example of JavaScript Array Methods

Table of contents Introduction Creating an Array ...

CentOS 7 set grub password and single user login example code

There are significant differences between centos7...

Use xshell to connect to the Linux server

Benefits of using xshell to connect to Linux We c...

Detailed explanation of redo log and undo log in MySQL

The most important logs in the MySQL log system a...

Solution for importing more data from MySQL into Hive

Original derivative command: bin/sqoop import -co...

TCP socket SYN queue and Accept queue difference analysis

First we must understand that a TCP socket in the...

The url value of the src or css background image is the base64 encoded code

You may have noticed that the src or CSS backgroun...

How to encapsulate axios in Vue

Table of contents 1. Installation 1. Introduction...

How to use Docker to build a development environment (Windows and Mac)

Table of contents 1. Benefits of using Docker 2. ...

MySQL 4 common master-slave replication architectures

Table of contents One master and multiple slaves ...

Let me teach you how to use font icons in CSS

First of all, what is a font icon? On the surface...

How to implement Mysql scheduled tasks under Linux

Assumption: The stored procedure is executed ever...

How to implement Linux deepin to delete redundant kernels

The previous article wrote about how to manually ...

Use of Linux tr command

1. Introduction tr is used to convert or delete a...