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 相关文章推荐
动态加载iframe
Jun 16 Javascript
xml文档转换工具,附图表例子(hta)
Nov 17 Javascript
js中hash和ico的关联分析
Feb 05 Javascript
跟我学习javascript的隐式强制转换
Nov 16 Javascript
理解javascript中的严格模式
Feb 01 Javascript
iframe中使用jquery进行查找的方法【案例分析】
Jun 17 Javascript
angular双向绑定模拟探索
Dec 26 Javascript
详解小程序中h5页面onShow实现及跨页面通信方案
May 30 Javascript
使用Node.js在深度学习中做图片预处理的方法
Sep 18 Javascript
vue-cli3.X快速创建项目的方法步骤
Nov 14 Javascript
tracking.js实现前端人脸识别功能
Apr 16 Javascript
Jquery ajax书写方法代码实例解析
Jun 12 jQuery
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
Php做的端口嗅探器--可以指定网站和端口
2006/10/09 PHP
php的ajax简单实例
2014/02/27 PHP
PHP函数积累总结
2019/03/19 PHP
PHP与SQL语句写一句话木马总结
2019/10/11 PHP
jQuery 获取URL参数的插件
2010/03/04 Javascript
浅析Prototype的模板类 Template
2011/12/07 Javascript
JavaScript设计模式之抽象工厂模式介绍
2014/12/28 Javascript
深入理解JavaScript系列(30):设计模式之外观模式详解
2015/03/03 Javascript
jquery实现的仿天猫侧导航tab切换效果
2015/08/24 Javascript
jQuery实现点击小图显示大图代码分享
2015/08/25 Javascript
js以分隔符分隔数组中的元素并转换为字符串的方法
2016/11/16 Javascript
AngularJS表格样式简单设置方法示例
2017/03/03 Javascript
详解VUE中常用的几种import(模块、文件)引入方式
2018/07/03 Javascript
jQuery实现获取form表单内容及绑定数据到form表单操作分析
2018/07/03 jQuery
Vue简单封装axios之解决post请求后端接收不到参数问题
2020/02/16 Javascript
vue实现简单瀑布流布局
2020/05/28 Javascript
element-ui点击查看大图的方法示例
2020/12/14 Javascript
解析Python中的__getitem__专有方法
2016/06/27 Python
Python3 venv搭建轻量级虚拟环境的步骤(图文)
2019/08/09 Python
关于Pytorch MaxUnpool2d中size操作方式
2020/01/03 Python
pytorch中的自定义反向传播,求导实例
2020/01/06 Python
离线状态下在jupyter notebook中使用plotly实例
2020/04/24 Python
Python调用JavaScript代码的方法
2020/10/27 Python
全面解析CSS Media媒体查询使用操作(推荐)
2017/08/15 HTML / CSS
浅谈html5与APP混合开发遇到的问题总结
2018/03/20 HTML / CSS
BONIA官方网站:国际奢侈品牌和皮革专家
2016/11/27 全球购物
internal修饰符起什么作用
2013/12/16 面试题
需求分析说明书
2014/05/09 职场文书
综治维稳工作承诺书
2014/08/30 职场文书
爱祖国爱家乡演讲稿
2014/09/02 职场文书
党支部群众路线整改措施思想汇报
2014/10/10 职场文书
教师求职自荐信范文
2015/03/04 职场文书
英语教师求职信范文
2015/03/20 职场文书
商场圣诞节活动总结
2015/05/06 职场文书
鉴史问廉观后感
2015/06/10 职场文书
《狼牙山五壮士》读后感:宁死不屈,视死如归
2019/08/16 职场文书