### 样式作用域
scoped
### vuex的辅助函数(Player.vue)
mapState:将state值直接映射到计算属性
原来拿到state中的songList:
computed: { songList(){ return this.$store.state.songList; } }
通过mapState拿到state中的songList:
computed: { ...mapState(["songList"]) }
步骤:
①引入
import {mapState,mapMutations,mapActions,mapGetters} from "vuex";
②computed中使用...mapState()拿到store中state中的属性,使用...mapGetters拿到store中getters中的方法
computed: { // songList(){ // return this.$store.state.songList.length; // } ...mapState(["songList","fullScreen","currentIndex"]), // currentSong(){ // return this.$store.getters.currentSong // } ...mapGetters(["currentSong"]) }
### 点击Detail.vue列表歌曲,跳转至Player.vue
①在store.js中,声明歌单列表和当前播放歌曲的下标
const store=new Vuex.Store({ state:{ playing:false, fullScreen:false, songList:[], // 歌单列表 currentIndex:-1, // 当前正在播放的歌曲 }, mutations:{ setSongList(state,list){ state.songList=list; }, setCurrentIndex(state,index){ state.currentIndex=index; } }, getters:{ currentSong(state){ // 根据下标获取当前播放歌曲的信息 return state.songList[state.currentIndex]; } } })
②Detail.vue中通过goPlayer()方法中的commit()触发mutations中的方法,将当前歌手的歌单列表和当前下标分别传过去
goPlayer(index){ this.$store.commit("setSongList",this.list); this.$store.commit("setCurrentIndex",index); }
③Player.vue中,可以通过getters拿到
{{this.$store.getters.currentSong}}
这个取值的方法太繁琐了,可以通过辅助函数mapGetters拿到:
computed: { ...mapGetters(["currentSong"]) }
然后就{{currentSong}}就可以了
最好是通过actions来触发,稍作修改:
store.js中:
actions:{ addSongList({commit},{index,list}){ commit("setSongList",list); commit("setCurrentIndex",index); } },
Detail.vue中:
goPlayer(index){ // this.$store.commit("setSongList",this.list); // this.$store.commit("setCurrentIndex",index); this.$store.dispatch("addSongList",{list:this.list,index:index}); }
注意:actions的用途就是处理异步和做多个commit的封装。
### 利用const将mutations中函数的名字做封装,避免命名重复(store.js)
①store文件夹下新建mutations-type.js文件:
const SET_SONG_LIST="SET_SONG_LIST"; const SET_CURRENT_INDEX="SET_CURRENT_INDEX"; export default{ SET_SONG_LIST, SET_CURRENT_INDEX }
②store.js中用常量名替代原来的函数名:
引入:
import type from "./mutations-type.js"
mutations和actions中:
mutations:{ [type.SET_SONG_LIST](state,list){ state.songList=list; }, [type.SET_CURRENT_INDEX](state,index){ state.currentIndex=index; } }, actions:{ addSongList({commit},{index,list}){ commit(type.SET_SONG_LIST,list); commit(type.SET_CURRENT_INDEX,index); } },
### utils工具的使用(Player.vue)
①在src下新建utils/formatUrl.js:
// 专辑 export const albumUrl=(albumMid)=>{ return `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg?max_age=2592000`; } // 歌曲 export const songUrl=(songMid)=>{ return `http://aqqmusic.tc.qq.com/amobile.music.tc.qq.com/C400${songMid}.m4a?guid=4887996690&vkey=16121C6B6E73C564FA01F98651C808B32B6D8788E8C7A1FCCFD030B194EC90658BB78D0A988B6DBC9F0C6E1535FD194E2C459CE01510E438&uin=0&fromtag=38`; }
②引入:(Player.vue)
import * as urlObj from "utils/formatUrl.js";
或
import {albumUrl,songUrl} from "utils/formatUrl.js";
注意:
1、export default{} 只能抛出一个对象,如果要抛出多个用 export const ...
2、引入的时候加上 * as,或者通过解构:import {albumUrl,songUrl} from "utils/formatUrl.js";
### &符号的意思(Player.vue)
img{ .w(300); .h(300); border-radius: 50%; border: 10px solid hsla(0,0%,100%,.1); box-sizing: border-box; &.play{ animation: rotate 20s linear infinite; } &.paused{ animation-play-state: paused; } }
加&表示play、paused和img是同级的,不加&表示play、paused是img的子级。
### audio标签的timeupdate事件:当前播放时间(Player.vue)
audio绑定timeupdate事件,可以通过e.target.currentTime拿到当前播放时间
①定义timeupdate事件
②将当前时间赋给current当前时间
timeUpdate(e){ console.log("时间变化",e.target.currentTime) this.current=e.target.currentTime; }
### 点击play按钮,实现播放和暂停,播放时img旋转,暂停时img暂停旋转(Player.vue)
①给img标签绑定类名playClass
if="currentSong" :src="albumUrl">
②按钮添加点击事件play
③data中声明playing为false
data() { return { playing:false } }
④设置play和paused类名的样式
img{ .w(300); .h(300); border-radius: 50%; border: 10px solid hsla(0,0%,100%,.1); box-sizing: border-box; &.play{ animation: rotate 20s linear infinite; } &.paused{ animation-play-state: paused; } }
⑤computed中将playing的值赋给playClass类
playClass(){ return this.playing?"play":"play paused"; }
接下来控制playing的布尔值就可以控制img的旋转
⑥play()方法中判断是否处于暂停中,如果是点击播放,否则点击暂停,同时设置playing值
play(){ let audio=this.$refs.audio; if(audio.paused){ audio.play(); this.playing=true; }else{ audio.pause(); this.playing=false; } console.log(audio.__proto__) }
注意:
1、添加类名的时候不可以写 return this.playing?"play":"paused"; 这样会造成暂停过后,img重新开始旋转。
2、打印audio标签的__proto__对象,可以获取audio的一系列属性和方法。
### 上一曲(prev)和下一曲(next)(Player.vue)
①添加prev和next按钮:
②通过commit()触发mutations中的SET_CURRENT_INDEX方法:
next(){ let index=this.currentIndex+1; this.$store.commit("SET_CURRENT_INDEX",index); }, prev(){ let index=this.currentIndex-1; this.$store.commit("SET_CURRENT_INDEX",index); }
③边界判断,当SET_CURRENT_INDEX方法中判断当index大于列表最后一项时,让index为0;当index小于列表第一项时,让index为列表最后一项。这就是循环播放:
[type.SET_CURRENT_INDEX](state,index){ if(index>state.songList.length-1){ index=0; }else if(index<0){ index=state.songList.length-1; } state.currentIndex=index; },
### audio标签的ended事件:播放完成触发(Player.vue)
①
②
ended(){ console.log("播放完成") }
### props传值(Player.vue、P-scrollBar.vue)
props用于父传子传值。
传递:当子组件在父组件中当做标签传值使用的时候,给当前子组件绑定一个自定义属性,值为需要传递的数据。
接收:在子组件内部通过props属性来进行接收。props接收的方式有数组和对象两种:
数组接收:
props:["msg"]
对象接收:
props:{ msg:{ type:Number, default:0, required:true } }
type:限制外部数据的类型
default:默认值,当父组件没有给子组件传值时用默认值
required:布尔值,当前属性是不是必传的值,如果值为true,不传msg属性时会报错
传递:(Player.vue)
接收:(P-scrollBar.vue)
props:{ "current":{type:Number,default:0}, "duration":{type:Number,default:0}, }
### watch监听控制进度条的宽度(P-scrollBar.vue)
watch: { current(newValue){ let percentage=(newValue/this.duration)*100; this.$refs.container.style.width=`${percentage}%`; } }
注意:watch中的方法是data中的属性,watch依赖于data,当data中的属性发生改变的时候,触发watch中函数执行。current()是data中的属性,这里它是由props传递过来的,当current()执行时,进度条宽度被不断地重新定义。
### 点击滚动条控制播放时间(P-scrollBar.vue)
①给wrapper盒子绑定点击事件clickDarg
②将比例赋给container盒子,并将这个百分比传给父组件Player.vue
clickDarg(e){ let percentage=(e.offsetX/this.$refs.wrapper.clientWidth)*100; this.$refs.container.style.width=`${percentage}%`; // 派发jump事件给Player.vue this.$emit("jump",percentage); }
③给PScrollBar组件绑定jump事件
④audio的currentTime属性可读也可写
jump(percentage){ this.$refs.audio.currentTime=this.duration*percentage/100; }