vue歌曲进度条示例代码

注意这个不是vue-cli创建的项目 是一个引用vue.js写的html文件 ,直接粘到一个html文件就能用了,我的音乐链接隔一段时间会失效,需要自己准备音乐
有拖动和点击切换播放进度的功能

demo图片

代码

<!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">播放</button>
    <button @click="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>

  <!-- 子组件 -->
  <template id="myCpn">

    <div class="progress-bar">
      <!-- 后面黑色的一条 -->
      <div class="bar-inner" @click="clickProgress">
        <!-- 已经播放的区域 -->
        <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
    // 子组件
    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) {
          // 进度条宽度
          const barWidth = this.$el.clientWidth - progressBtnWidth
          this.offset = barWidth * newProgress
        }
      },
      methods: {
        onTouchStart(e) {
          // console.log(e);
          this.touch.x1 = e.changedTouches[0].clientX
          // 黄色进度条初始宽度
          this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchMove(e) {
          // console.log(e);
          // x偏移量
          const delta = e.changedTouches[0].clientX - this.touch.x1
          // 之前的width+这次拖动增加的偏移量=应有的黄条长度
          const tempWidth = this.touch.beginWidth + delta
          // 再拿到barWidth
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // 黄条长度/barwidth = progress 现在应该有的进度
          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)
        },
        // 点击进度条
        clickProgress(e){
          // console.log("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          const rect = this.$el.getBoundingClientRect()
          // 黄条应有的宽度
          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
          // 默认暂停
          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
          // 实时修改currentTime值
          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 向下取整
          interval = interval | 0
          // 不足两位的话就向前填充一个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;
  }

  /* 子组件样式 */
  .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>

解说

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

中间的进度条是一个进度条组件,一个黑色的背景是进度的总长度,左侧黄色的条是当前播放的进度,中间的滑块是可以左右拖动的,可以手动改变进度条,在播放的过程中,进度条是会变长的,并且滑块是向右偏移的,可以左右拖动滑块,拖动也是改变了播放进度,并且左侧的时间是会发生变化的

来实现播放过程中,进度条也会随之播放 组件的状态靠什么决定呢 可以靠进度来决定,组件的任何状态都可以根据进度来决定,父组件传入一个数字类型的progress

btn的位置,以及progress黄条的宽度都是根据progress计算而来的,宽度可以用一个数据offset来表示(定义个data),之后要监听progess,

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

知识 获取根 DOM 元素

      watch: {
        progress(newProgress) {
          // 进度条宽度
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // 偏移量
          this.offset = barWidth * newProgress
        }
      }

知识 当然可以用computed,但是要注意用computed获取el的宽度一开始肯定是获取不到的,computed一开始上来就计算一次,在模板被渲染的时候就会访问offset,然后就会计算一次el宽度,这时候组件还没有mounted,是获取不到的;watch的话,progress变化的时候其实已经渲染了,所以clientWidth就可以拿到,另外,因为之后还要处理一些逻辑,更偏向逻辑的编写,所以应该用watch去实现

有了offset之后要去映射dom,给黄色进度条和btn设置一个动态的style,

他们两个的style都是根据offset计算而来的,

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

现在来根据offset来计算出它的样式是怎么样的 我们接受progress这个属性,当外部的progress变了之后,就根据progress计算出它的offset,有了偏移量,样式就能发生变化,

疑问 flex 0 0 40px 与width 两者效果是类似的,但是在某些场合下,flex布局会出现挤压或塌陷的现象,导致宽度被挤压,所以设定width可以保证我们的宽度不变化

这里是监听canplay事件

父组件计算属性 播放进度:已播放时间/总时间 总时间已经拿到了,播放时间可以用一个事件:timeupdate来监听

现在的效果

可以看出来这是秒数,需要格式化时间,定义一个工具函数

插播 函数柯里化 https://www.jianshu.com/p/2975c25e4d71 IIFE:自我执行函数 柯里化
还有位运算一些东西 https://www.jianshu.com/p/a3202bc3f7a4

一个疑问 xxx.yyy|0 为什么等于xxx 为什么这里或运算符能有取整的作用呢

知识padstart方法

formatTime函数

formatTime(interval) {
  // interval 向下取整
  interval = interval | 0
  // 不足两位的话就向前填充一个0
  const minute = ((interval / 60 | 0) + '').padstart(2, '0')
  const second = ((interval % 60 | 0) + '').padstart(2, '0')
  return `${minute}:${second}`
}

但是并不能用 它识别不了这个padstart方法

所以只能自己写了

        formatTime(interval) {
          // interval 向下取整
          interval = interval | 0
          // 不足两位的话就向前填充一个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}`
        }

接下来写进度条的交互逻辑

支持拖动和点击

在移动端常见的就是ontouchstart ontouchmove ontouchend
https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent

知识 prevent修饰符

给滑块添加三个事件

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

需要获取两个信息,一个是要知道它点击的位置,也就是说要知道他的横坐标是什么。以及左侧进度条的宽度(offset)

[screenX clientX pageX概念

因为横坐标的位置在touchmove的时候也需要获取,所以可以把数据绑定到一个可以被共享的对象上,可以在created钩子函数中定义一个对象,

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

给黄条一个ref 之后

        onTouchStart(e) {
          // console.log(e);
          this.touch.x1=e.changedTouches[0].clientX
          // 黄色进度条初始宽度
          this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchStart(e) {
          // console.log(e);
          this.touch.x1=e.changedTouches[0].clientX
          // 黄色进度条初始宽度
          this.touch.beginWidth = this.$refs.progress.clientWidth
          console.log(this.touch);
        },
        onTouchMove(e) {
          // console.log(e);
          // x偏移量
          const delta = e.changedTouches[0].clientX-this.touch.x1
          // 之前的width+这次拖动增加的偏移量=应有的黄条长度
          const tempWidth = this.touch.beginWidth + delta
          // 再拿到barWidth
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // 黄条长度/barwidth = progress 现在应该有的进度
          const progress = tempWidth/barWidth
          this.offset = barWidth * progress
          // console.log("tempWidth", tempWidth);
          // console.log("barWidth", barWidth);
          // console.log("progress", progress);

        },

来整理一下,最终目的是要拿到offset,offset是由progress和barWidth共同决定的,这里progress怎么算呢需要拿到当前黄条应该的宽度除总宽度,黄条应该的宽度就是一开始的宽度+这次滑动的x距离,然后barWidth的获取是简单的,之后就可以算出来了

会不会觉得多此一举呢 直接原来的黄条宽度+这次滑动的长度不就可以了吗 为什么还要算progress呢,因为要让外部知道,歌曲的进度发生了改变,要让他们对应上才可以,最终是要修改audio的,这个是用父组件做的,现在只是实现了拖动,所以需要派发事件,这里派发两个自定义事件,一个progress-changing事件,表示手指还在拖动的过程中,还没有离开,当手指离开的时候还要派发一个progress-change 把新的progress传出去

实时修改currentTime的值

这是拖动的时候修改currentTIme,修改音乐的时间是在手松开的时候,

但是我们暂停的时候发现是可以拖动的,但是播放的时候拖动发现是有问题的,

优化:在change的时候,如果是暂停的效果就让他播放,这时候就要定义一个isplay在点击播放暂停的时候翻转

现在来改bug,在播放的时候,拖动进度会出问题,为什么呢,监听progressChanging,我们修改了currentTime,这个currentTime一旦发生了改变,progress会根据currentTime做一个新的计算,然后传给子组件,子组件他就会进入到这个逻辑

offset就会重新做一次计算,

最后这里会覆盖

应该在update的时候需要做一些控制,在changing的过程加一个标志位,

就是说在update函数中,如果changing在拖动的过程中,不要去修改currentTime,在changing的过程中,就认为是进度条改变,他修改进度条的优先级高,自身播放导致的currentTime改变优先级比较低,

这样就ok了

除了拖动,我们还希望点击它跳转到对应位置,

知识webapi --getBoundingClientRect 方法返回元素的大小及其相对于视口的位置(获取短的那一条)。

用pagex获取长的那一条

        clickProgress(e){
          // console.log("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          const rect = this.$el.getBoundingClientRect()
          // 黄条应有的宽度
          const offsetWidth = e.pageX - rect.x
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const progress = offsetWidth/barWidth
          this.$emit('progress-changed', progress)
          console.log(offsetWidth)
        }

到此这篇关于vue歌曲进度条demo的文章就介绍到这了,更多相关vue歌曲进度条内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue2.0实现音乐/视频播放进度条组件

    基于vue2.0实现音乐/视频播放进度条组件的方法及代码解释,具体内容如下 需求分析: ①:进度条随着歌曲的播放延长,歌曲播放完时长度等于黑色总进度条长度:时间实时更新. ②:当滑动按钮时,实时更新播放时间,橙色进度条长度也会随着按钮的滑动而改变,当滑动结束时,橙色区域停留在滑动结束的位置,歌曲从当前进度开始播放. ③:点击进度条,橙色进度条长度变为点击处至起点的长度,并从当前点开始播放歌曲. 大概思路: ①:左边的时间可以通过audio播放时派发的timeupdate事件获取,右边的时间为接口

  • vue2.0+SVG实现音乐播放圆形进度条组件

    vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果 需求分析: 类似于大多数音乐播放器中等mini播放器控制按钮,显示播放进度,实时更新进度. progress-circle.vue源码: <template> <div class="progress-circle"> <svg :width="radius" :height="radius" viewBox="0 0 10

  • vue页面加载时的进度条功能(实例代码)

    先看一张图 如果我们的程序每次页面切换时,顶部也有一个进度条,那会让用户体验提升很大的. npropgress插件 github地址 简单用法 - Vue 项目为例(详细配置,点击上面的github地址查看文档) 最简单的使用方式:vue项目的每次路由切换时,都加载进度条 安装 npm install --save nprogress 引入 在路由配置文件中引入,写到路由加载前和加载后的两个钩子函数中 // 引入 import NProgress from 'nprogress'; import

  • vue.js+ElementUI实现进度条提示密码强度效果

    要求一:判断输入的字符串是否包含数字.小写字母.大写字母以及特殊字符四种内容的8-20位字符 通过搜索了解到可以使用?=这个正则语法判断字符串中是否含有多种内容.(?=)这个语法结构在正则里表示"设定后面是"的意思,举下面几个例子进一步了解?=这个语法: (?=.*[a-zA-Z])  这句的意思就是后面必须有一位大写或小写字母 (?=.*[1-9]) 这句的意思是后面必须有一位数字 (?=.*[\W]) 这句的意思是后面必须有一个非字母数字及下划线的特殊符号 (?!.*[\u4E00

  • vue歌曲进度条示例代码

    注意这个不是vue-cli创建的项目 是一个引用vue.js写的html文件 ,直接粘到一个html文件就能用了,我的音乐链接隔一段时间会失效,需要自己准备音乐 有拖动和点击切换播放进度的功能 demo图片 代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible

  • Spring Boot+AngularJS+BootStrap实现进度条示例代码

    Spring Boot+AngularJS+BootStrap实现进度条 原理 进度条的原理是在上传文件的时候,当程序运行到某一个部分,往Session中设置一个1到100的值.然后前台再每隔很小的一段时间去请求这个值. 在AngularJS中,$http对象有3种状态,分别是success,progress,error,其中progress方法就会在success方法调用之前(也就是上传完成之前),不断地调用.而我们要做的就是在progress中在添加一个请求,去后台拿我们设置在session

  • servlet+jquery实现文件上传进度条示例代码

    现在文件的上传,特别是大文件上传,都需要进度条,让客户知道上传进度. 本文简单记录下如何弄进度条,以及一些上传信息,比如文件的大小,上传速度,预计剩余时间等一些相关信息.代码是匆忙下简单写的,一些验证没做,或代码存在一些隐患,不严谨的地方.本文代码只供参考. 进度条的样式多种多样,有些网站弄得非常绚烂漂亮.本文UI端不太懂,只会一些简单的基本的css而已,所以进度条弄得不好看.本文侧重的给读者提供一个参考,一个实现思路而已. 注:由于jQuery版本用的是2.1.1,所以如果跑本例子源码,请用I

  • ajax提交加载进度条示例代码

    实现效果图  加载图片  实现方式: 在jsp页面中加入 样式 复制代码 代码如下: .progress{z-index: 2000} .mask{position: fixed;top: 0;right: 0;bottom: 0;left: 0; z-index: 1000; background-color: #2F2F2F} 节点 复制代码 代码如下: <img id="progressImgage" class="progress" style=&qu

  • Python一行代码快速实现程序进度条示例

    目录 引言 1.先上代码 2.使用说明 3.实现原理 引言 你在写代码的过程中,有没有遇到过以下问题? 已经写好的程序,想看看程序执行的进度? 在写代码批量处理文件的时候,如何显示现在处理到第几个文件了? 如上图所示的进度条是一个最好的解决方法,怎么在不修改原来代码的情况下,快速给程序加一个进度条呢? 今天我们来学习一个最简单的方法~ 1.先上代码 下载进度条的第三方库. pip install poprogress 使用这个库,快速制作进度条 from poprogress import si

  • Python进度条实时显示处理进度的示例代码

    前言 在大多数时候,我们的程序会一直进行循环处理.这时候,我们非常希望能够知道程序的处理进度,由此来决定接下来该做些什么.接下来告诉大家如何简单又漂亮的实现这一功能. 如何使用这个类 使用这个类很简单,只需要三步即可完成,如下: process_bar = ShowProcess(max_steps) # 1.在循环前定义类的实体, max_steps是总的步数 for i in range(max_steps + 1): process_bar.show_process() # 2.显示当前进

  • Android自定义View实现带数字的进度条实例代码

    第一步.效果展示 图1.蓝色的进度条 图2.红色的进度条 图3.多条颜色不同的进度条 图4.多条颜色不同的进度条 第二步.自定义ProgressBar实现带数字的进度条 0.项目结构 如上图所示:library项目为自定义的带数字的进度条NumberProgressBar的具体实现,demo项目为示例项目以工程依赖的方式引用library项目,然后使用自定义的带数字的进度条NumberProgressBar来做展示 如上图所示:自定义的带数字的进度条的library项目的结构图 如上图所示:de

  • jquery实现模拟百分比进度条渐变效果代码

    本文实例讲述了jquery实现模拟百分比进度条渐变效果代码.分享给大家供大家参考,具体如下: 这里为了便于看到加载百分比,对代码进行了处理,实际使用时并不需要这样. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/jquery-mn-bfb-scroll-cha-style-demo/ 具体代码如下: <html> <head> <title>jquery模拟百分比进度条</title> <script

  • vue Nprogress进度条功能实现常见问题

    NProgress是页面跳转是出现在浏览器顶部的进度条 官网:http://ricostacruz.com/nprogress/ github:https://github.com/rstacruz/nprogress 下图中的这种顶部进度条是非常常见的,在vue项目中有对应的插件.Nprogress Nprogress进度条的使用方法如下: 1.安装nprogress插件 npm install --save nprogress 注意此处的--save等同于-s,就是将插件的名称及版本号保存到

  • Python编程使用PyQt5库实现动态水波进度条示例

    目录 原理介绍 代码实操 最近做了一个小项目,里面有一个需求需要添加一个动态进度条,进度条的样式就类似于水波来回起伏的那种形状,下面就是最初的展示效果(有一点区别,这里我加了一个进度自动增加的功能): 下面先说一下这个效果的制作原理 原理介绍 在介绍动态效果之前需要先看一下静态的: 如果仔细观察的话,静态图效果的呈现在于先后的两个线条的绘制,产生水波的主要是由于两线条的左右的水平错位 以及 设置的透明度不同 所造成的: 想要形成最后的水波荡漾的视觉效果,只需要把数张线条连续走向的静态图拼接在一起

随机推荐