本文实例为大家分享了关于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实战教程》
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
暂无评论内容