### 样式作用域
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=newVuex.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){//根据下标获取当前播放歌曲的信息
returnstate.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";
exportdefault{
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
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按钮:
prev
play
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;
}