vue-music关于Player播放器组件详解


Posted in Javascript onNovember 28, 2017

本文实例为大家分享了关于Player播放器组件的具体内容,供大家参考,具体内容如下

vue-music关于Player播放器组件详解

迷你播放器:

vue-music关于Player播放器组件详解

1.播放器组件会在各个页面的情况下会打开。 首先在vuex state.js 中定义全局的播放器状态

import {playMode} from 'common/js/config.js';

const state = {
 singer:{}, 
 playing:false, //是否播放
 fullScreen:false, //是否全屏
 playList:[], //播放列表
 sequenceList:[], // 非顺序播放列表
 mode:playMode.sequence, // 播放模式(顺序0,循环1,随机2)
 currentIndex:-1, //当前播放索引
}
export default state 


---------------------------------------------
// config.js

export const playMode = {
 sequence:0,
 loop:1,
 random:2
}

2.进入播放器页面时获取播放列表数据,改变播放状态   在music-list列表中打开

在song-list 组件中派发事件到父组件,传入当前歌曲的信息和索引

<li @click="selectItem(song,index)" v-for="(song,index) in songs" class="item">

------------------------------
selectItem(item,index){
 this.$emit('select',item,index)
},

在music-list 组件中接受派发事件。

<song-list :rank="rank" :songs="songs" @select="selectItem"></song-list>

3. 如果commit 多个状态在actions 里设置

import {playMode} from 'common/js/config.js'

export const selectPlay = function({commit,state},{list,index}){
 commit(types.SET_SEQUENCE_LIST, list)
 commit(types.SET_PLAYLIST, list)
 commit(types.SET_CURRENT_INDEX, index)
 commit(types.SET_FULL_SCREEN, true)
 commit(types.SET_PLAYING_STATE, true)
}

4. 在music-list 组件中 用mapActions提交 改变值

import {mapActions} from 'vuex'

methods:{
 selectItem(item,index){
  this.selectPlay({
  list:this.songs,
  index
  })
 },
 ...mapActions([
  'selectPlay'
 ])
 },

5.在palyer 中获取vuex 全局状态,赋值状态到相应位置(代码为完整代码,对照后面讲解慢慢理解)

<div class="player" v-show="playList.length>0">
// 如果有列表数据则显示
  <div class="normal-player" v-show="fullScreen">
//如果全屏
   <div class="background">
   <img :src="currentSong.image" alt="" width="100%" height="100%">

//模糊背景图
   </div>
   <div class="top">
    <div class="back" @click="back">
    <i class="icon-back"></i>
    </div>
    <h1 class="title" v-html="currentSong.name"></h1>

//当前歌曲名称
    <h2 class="subtitle" v-html="currentSong.singer"></h2>
//当前歌手名
   </div>
   <div class="middle">
    <div class="middle-l">
    <div class="cd-wrapper">
     <div class="cd" :class="cdCls">
     <img :src="currentSong.image" alt="" class="image">

//封面图
     </div>
    </div>
    </div>
   </div>
   <div class="bottom">
   <div class="progress-wrapper">
   <span class="time time-l">{{ format(currentTime) }}</span>
   <div class="progress-bar-wrapper">
    <progress-bar :percent="percent" @percentChange="onProgressBarChange"></progress-bar>
   </div>
   <span class="time time-r">{{ format(currentSong.duration) }}</span>
   </div>
     <div class="operators">
    <div class="icon i-left">
     <i :class="iconMode" @click="changeMode"></i>
    </div>
     <div class="icon i-left" :class="disableCls">
      <i @click="prev" class="icon-prev"></i>
      </div>
      <div class="icon i-center" :class="disableCls">
      <i :class="playIcon" @click="togglePlaying"></i>
      </div>
      <div class="icon i-right" :class="disableCls">
      <i @click="next" class="icon-next"></i>
      </div>
      <div class="icon i-right">
      <i class="icon icon-not-favorite"></i>
      </div>
    </div>
    </div>
   </div>
   </transition>
   <transition name="mini">
    <div class="mini-player" v-show="!fullScreen" @click="open">
     <div class="icon">
      <img :src="currentSong.image" alt="" width="40" height="40" :class="cdCls">
     </div>
     <div class="text">
      <h2 class="name" v-html="currentSong.name"></h2>
      <p class="desc" v-html="currentSong.singer"></p>
     </div>
     <div class="control">
      <i :class="miniIcon" @click.stop="togglePlaying"></i>
     </div>
     <div class="control">
      <i class="icon-playlist"></i>
     </div>
    </div>
   </transition>
   <audio :src="currentSong.url" ref="audio" @canplay="ready" @error="error" @timeupdate="updateTime" @ended="end"></audio>
 </div>

打开播放器的状态

import {mapGetters,mapMutations} from 'vuex';

...mapGetters([
 'fullScreen',
 'playList',
 'currentSong',
 'playing',
 'currentIndex',
])

注意:不可在组件中直接赋值改版vuex 中的状态 this.fullScreen = false 需要通过mutations 改变,定义mutation-types 和mutations 然后 用vuex的 mapMutations 代理方法调用

[types.SET_FULL_SCREEN](state, flag) {
 state.fullScreen = flag
 },

import {mapGetters,mapMutations} from 'vuex';
methods:{
 ...mapMutations({
 setFullScreen:"SET_FULL_SCREEN", 
 }),
 back(){
 this.setFullScreen(false)
 },
}

设置点击播放按钮方法

<i :class="playIcon" @click="togglePlaying"></i>
togglePlaying(){
 this.setPlayingState(!this.playing); //改变全局变量playing 的属性
},

// 然后watch 监听playing 操作实际的audio 标签的播放暂停
watch:{
  playing(newPlaying){
   let audio = this.$refs.audio;
   this.$nextTick(() => {
    newPlaying ? audio.play():audio.pause();
   })
  }
 },

// 用计算属性改变相应的播放暂停图标
playIcon(){
 return this.playing? 'icon-pause':'icon-play'
},

设置点击播放上一首和下一首按钮方法。用mapGetters 获取currentIndex 的值(加一或减一) 并改变,从而改变 currentSong 的状态,监听切换播放。判断播放列表界限重置。

prev(){

if(!this.songReady){



return;


}

 let index = this.currentIndex - 1;
 if(index === -1){

//判断播放列表界限重置
  index = this.playList.length-1;
 }
 this.setCurrentIndex(index);
 if(!this.playing){
//判断是否播放改变播放暂停的icon
  this.togglePlaying();
 }
 this.songReady = false;
},
next(){
 if(!this.songReady){


return;

 }
 let index = this.currentIndex + 1;
 if(index === this.playList.length){

//判断播放列表界限重置
  index = 0;
 }
 this.setCurrentIndex(index);
 if(!this.playing){
  this.togglePlaying();
 }
 this.songReady = false;
},

监听audio 元素标签的canpaly 事件,当歌曲加载就绪 和 error 事件,当歌曲发生错误的时候,做用户体验,防止用户快速切换导致报错。

设置songReady 标志位 如果歌曲没有准备就绪,点击下一首的时候直接return false

data(){
 return {
  songReady:false,
 }
},

ready(){
 this.songReady = true;
},
error(){
 this.songReady = true;
},

进度条

audio元素监听 timeupdate 事件获取当前播放时间的 可读写属性 时间戳。用formt做格式化时间处理,(_pad 为补零函数 )

获取音频总时长 currentSong.duration

<div class="progress-wrapper">
 <span class="time time-l">{{ format(currentTime) }}</span>
 <div class="progress-bar-wrapper">
 <progress-bar :percent="percent" @percentChange="onProgressBarChange"></progress-bar>
 </div>
 <span class="time time-r">{{ format(currentSong.duration) }}</span>
</div>

<audio :src="currentSong.url" ref="audio" @canplay="ready" @error="error" @timeupdate="updateTime" @ended="end"></audio>
updateTime(e){
 this.currentTime = e.target.currentTime; // 获取当前播放时间段
},

format(interval){
 interval = interval | 0;
 const minute = interval/60 | 0;
 const second = this._pad(interval % 60);
 return `${minute}:${second}`;  
},

_pad(num,n=2){
 let len = num.toString().length;
 while(len<n){
 num = '0' + num;
 len ++;
 }
 return num;
},

建立progress-bar 组件 接收pencent 进度参数,设置进度条宽度和小球的位置。player组件 设置计算属性percent

percent(){
 return this.currentTime / this.currentSong.duration  // 当前时长除以总时长
},

progress-bar 组件

<div class="progress-bar" ref="progressBar" @click="progressClick">
 <div class="bar-inner">
  <div class="progress" ref="progress"></div>
  <div class="progress-btn-wrapper" ref="progressBtn"
   @touchstart.prevent="progressTouchStart"
   @touchmove.prevent="progressTouchMove"
   @touchend="progressTouchEnd"  
  >
  <div class="progress-btn"></div>
  </div>
 </div>
 </div>
const progressBtnWidth = 16 //小球宽度

props:{
 percent:{
 type:Number,
 default:0
 }
},


watch:{
 percent(newPercent){
 if(newPercent>=0 && !this.touch.initated){

  const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
  const offsetWidth = newPercent * barWidth;
  this.$refs.progress.style.width = `${offsetWidth}px`;
  this.$refs.progressBtn.style.transform=`translate3d(${offsetWidth}px,0,0)`
 }
 }
}

设置拖动

在进度条小按钮progressBtn 上添加touchstart,touchmove,touchend 事件监听方法,事件添加 prevent 防止拖动默认浏览器行为,获取拖动的信息进行计算

在实例上创建一个touch 对象维护不同的回调之间的通讯共享状态信息。  touchstart事件方法中 ,首先设置this.touch.initated为true,表示拖动开始。  记录开始点击位置 e.touches[0].pageX 存到 touch 对象上,记录当前的进度宽度。

在touchmove 中首先判断 是否先进入了 touchstart 方法,计算得到 移动的位置 减去 点击开始的位置的 偏移量长度。 let deltax = e.touches[0].pageX - this.touch.startX

就可以 设置进度条 已有的长度加上偏移量长度。最大不能超过父级progressbar 的宽度

调用this._offset(offsetWidth) 方法设置进度条宽度

在touchend 事件方法中将 this.touch.initated 设置为false,表示拖动结束,并派发事件到player 组件将audio的currentTime 值设置为正确值,参数为pencent

在progressbar 中增加点击事件,调用this._offset(e.offsetX),并且派发事件

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

methods:{
 progressTouchStart(e){
 this.touch.initiated = true;
 this.touch.startX = e.touches[0].pageX;
 this.touch.left = this.$refs.progress.clientWidth;
 },
 progressTouchMove(e){
 if(!this.touch.initiated){
  return;
 }
 let deltaX = e.touches[0].pageX - this.touch.startX;
 let offsetWidth = Math.min(this.$refs.progressBar.clientWidth - progressBtnWidth,Math.max(0,this.touch.left + deltaX));
 this._offset(offsetWidth);
 },
 progressTouchEnd(e){
 this.touch.initiated = false;
 this._triggerPercent();
 },
 progressClick(e){
 const rect = this.$refs.progressBar.getBoundingClientRect();
 const offsetWidth = e.pageX - rect.left;
 this._offset(offsetWidth);
 // this._offset(e.offsetX);
 this._triggerPercent();
 },
 _offset(offsetWidth){
 this.$refs.progress.style.width = `${offsetWidth}px`;
 this.$refs.progressBtn.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
 },
 _triggerPercent(){
 const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
 const percent = this.$refs.progress.clientWidth / barWidth;
 this.$emit("percentChange",percent)
 }
},

本文已被整理到了《Vue.js前端组件学习教程》,欢迎大家学习阅读。

关于vue.js组件的教程,请大家点击专题vue.js组件学习教程进行学习。

更多vue学习教程请阅读专题《vue实战教程》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript学习历程和心得小结
Aug 16 Javascript
js中eval详解
Mar 30 Javascript
可在线编辑网页文字效果代码(单击)
Mar 02 Javascript
用html5 js实现点击一个按钮达到浏览器全屏效果
May 28 Javascript
javascript实现禁止复制网页内容
Dec 16 Javascript
JS中多步骤多分步的StepJump组件实例详解
Apr 01 Javascript
jQuery Validate验证框架详解(推荐)
Dec 17 Javascript
使用Webpack提高Vue.js应用的方式汇总(四种)
Jul 10 Javascript
详解vantUI框架在vue项目中的应用踩坑
Dec 06 Javascript
js实现转动骰子模型
Oct 24 Javascript
如何正确解决VuePress本地访问出现资源报错404的问题
Dec 03 Vue.js
vue实现可以快进后退的跑马灯组件
Apr 08 Vue.js
JavaScript数据结构之双向链表和双向循环链表的实现
Nov 28 #Javascript
JS实现的找零张数最小问题示例
Nov 28 #Javascript
JavaScript数据结构之单链表和循环链表
Nov 28 #Javascript
微信小程序tabBar模板用法实例分析【附demo源码下载】
Nov 28 #Javascript
关于angularJs清除浏览器缓存的方法
Nov 28 #Javascript
AngularJs 禁止模板缓存的方法
Nov 28 #Javascript
微信小程序自定义toast实现方法详解【附demo源码下载】
Nov 28 #Javascript
You might like
正义联盟的终局之战《天启星战争》将成为DC动画宇宙的最后一部
2020/04/09 欧美动漫
使用php4加速网络传输
2006/10/09 PHP
PHP中返回引用类型的方法
2015/04/03 PHP
php验证码生成代码
2015/11/11 PHP
Symfony2使用第三方库Upload制作图片上传实例详解
2016/02/04 PHP
Zend Framework入门应用实例详解
2016/12/11 PHP
php使用ftp实现文件上传与下载功能
2017/07/21 PHP
浅谈PHP中的Trait使用方法
2019/03/22 PHP
Laravel 已登陆用户再次查看登陆页面的自动跳转设置方法
2019/09/30 PHP
浅谈laravel中的关联查询with的问题
2019/10/10 PHP
发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser
2007/11/30 Javascript
JS+flash实现chrome和ie浏览器下同时可以复制粘贴
2013/09/22 Javascript
jQuery Ajax()方法使用指南
2014/11/19 Javascript
JavaScript使用Replace进行字符串替换的方法
2015/04/14 Javascript
javascript常见数据验证插件大全
2015/08/03 Javascript
BootStrap下拉框在firefox浏览器界面不友好的解决方案
2016/08/18 Javascript
微信小程序(三):网络请求
2017/01/13 Javascript
js实现移动端导航点击自动滑动效果
2017/07/18 Javascript
ES6正则表达式扩展笔记
2017/07/25 Javascript
vue中的计算属性的使用和vue实例的方法示例
2017/12/04 Javascript
从Vuex中取出数组赋值给新的数组,新数组push时报错的解决方法
2018/09/18 Javascript
element el-input directive数字进行控制
2018/10/11 Javascript
python 判断是否为正小数和正整数的实例
2017/07/23 Python
Windows下的Python 3.6.1的下载与安装图文详解(适合32位和64位)
2018/02/21 Python
详解分布式任务队列Celery使用说明
2018/11/29 Python
python自动化工具之pywinauto实例详解
2019/08/26 Python
Python如何省略括号方法详解
2020/03/21 Python
windows下的pycharm安装及其设置中文菜单
2020/04/23 Python
详解CSS3:overflow属性
2020/11/17 HTML / CSS
德国游戏机商店:Konsolenkost
2019/12/08 全球购物
《长江之歌》教学反思
2014/04/17 职场文书
煤矿安全生产月活动总结
2014/07/05 职场文书
暗恋桃花源观后感
2015/06/12 职场文书
2015中秋节晚会主持词
2015/07/01 职场文书
村官2015年度工作总结
2015/10/14 职场文书
Python利用zhdate模块实现农历日期处理
2022/03/31 Python