vue-music 关于Player (播放器组件)--播放和进度条

vue

迷你播放器

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-music 关于Player (播放器组件)--播放和进度条 的全部内容, 来源链接: utcz.com/z/376279.html

回到顶部