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
}
<li @click="selectItem(song,index)" v-for="(song,index) in songs" class="item">
------------------------------
selectItem(item,index){
this.$emit('select',item,index)
},
<song-list :rank="rank" :songs="songs" @select="selectItem"></song-list>
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)
}
import {mapActions} from 'vuex'
methods:{
selectItem(item,index){
this.selectPlay({
list:this.songs,
index
})
},
...mapActions([
'selectPlay'
])
},
<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',
])
[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'
},
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;
},
data(){
return {
songReady:false,
}
},
ready(){
this.songReady = true;
},
error(){
this.songReady = true;
},
<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;
},
percent(){
return this.currentTime / this.currentSong.duration // 当前时长除以总时长
},
<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)`
}
}
}
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)
}
},
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有