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 相关文章推荐
提高网站信任度的技巧
Oct 17 Javascript
javascript 无提示关闭窗口脚本
Aug 17 Javascript
Dojo 学习笔记入门篇 First Dojo Example
Nov 15 Javascript
javascript Object与Function使用
Jan 11 Javascript
在网站上应该用的30个jQuery插件整理
Nov 03 Javascript
使用jquery实现IE下按backspace相当于返回操作
Mar 18 Javascript
js树插件zTree获取所有选中节点数据的方法
Jan 28 Javascript
jquery中change()用法实例分析
Feb 06 Javascript
node.js中module.exports与exports用法上的区别
Sep 02 Javascript
javascript入门之数组[新手必看]
Nov 21 Javascript
Electron + vue 打包桌面操作流程详解
Jun 24 Javascript
Vue Extends 扩展选项用法完整实例
Sep 17 Javascript
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
cakephp打印sql语句的方法
2015/02/13 PHP
php自定义hash函数实例
2015/05/05 PHP
php与阿里云短信接口接入操作案例分析
2020/05/27 PHP
关于B/S判断浏览器断开的问题讨论
2008/10/29 Javascript
页面中js执行顺序
2009/11/09 Javascript
一次失败的jQuery优化尝试小结
2011/02/06 Javascript
如何将php数组或者对象传递给javascript
2014/03/20 Javascript
PHP中使用微秒计算脚本执行时间例子
2014/11/19 Javascript
js简单实现点击左右运动的方法
2015/04/10 Javascript
基于socket.io+express实现多房间聊天
2016/03/17 Javascript
Vuejs 组件——props数据传递的实例代码
2017/03/07 Javascript
完美解决linux下node.js全局模块找不到的情况
2018/05/16 Javascript
详解react native页面间传递数据的几种方式
2018/11/07 Javascript
vscode 插件开发 + vue的操作方法
2020/06/05 Javascript
[01:00:10]完美世界DOTA2联赛PWL S2 FTD vs Inki 第二场 11.21
2020/11/24 DOTA
python爬虫常用的模块分析
2014/08/29 Python
Python数据分析中Groupby用法之通过字典或Series进行分组的实例
2017/12/08 Python
详解用TensorFlow实现逻辑回归算法
2018/05/02 Python
Python全栈之列表数据类型详解
2019/10/01 Python
python创建子类的方法分析
2019/11/28 Python
python numpy 反转 reverse示例
2019/12/04 Python
解决Django部署设置Debug=False时xadmin后台管理系统样式丢失
2020/04/07 Python
python selenium xpath定位操作
2020/09/01 Python
通过代码简单了解django model序列化作用
2020/11/12 Python
css3中仿放大镜效果的几种方式原理解析
2020/12/03 HTML / CSS
五个2015 年最佳HTML5 框架
2015/11/11 HTML / CSS
美国眼镜网站:LensCrafters
2020/01/19 全球购物
C/C++程序员常见面试题一
2012/12/08 面试题
.net面试题
2016/09/17 面试题
自荐信怎么写好
2013/11/11 职场文书
信用社员工先进事迹材料
2014/02/04 职场文书
自动化专业毕业生求职信
2014/06/18 职场文书
卫生院艾滋病宣传活动小结
2014/07/09 职场文书
酒店管理失职检讨书
2014/09/16 职场文书
2016年教师政治思想表现评语
2015/12/02 职场文书
Python数据分析入门之教你怎么搭建环境
2021/05/13 Python