VUE练手项目实现音乐播放器(三)-------排行榜明细组件

今天,我们接着上一节继续向下进行,完成当用户点击自己想听的排行榜时,显示的界面 ,该界面要讲解的知识点较多。文件位置:src\components\RankPage.vue ,效果图:

 

               

 

讲解重点:

1、由 Rank.vue 页面传来的 排行榜id ,获取相应排行榜的歌单(这个页面的前端布局细节有点多,fixed定位、元素居中、设置背景渐变色、判断背景是否为深色、移动端1px边框过粗等)。

2、点击 排行榜标题右侧的 较大播放按钮,将全部歌单加入到 播放列表中。

3、 点击歌曲右侧的 ‘三点图片’,弹出 weui 的 ActionSheet ,并显示菜单(上面右图效果)。

4、 任意点击歌曲,实现歌曲的播放功能,用到 qm-player 。

5、CSS3 的 transition 过渡效果以及 transform的 translate

 

1、vue-router 路由传参:

 

router.js 给 RankPage 页面设置如下路由:

{path:'/rankpage/:id', name: 'rankpage', component: RankPage}        //path 属性中 ':id' 前也有个 '/'

 

Rank.vue 页面设置路由跳转:

this.$router.push({name: 'rankpage', params: {id : id}})        //笔者测试过,传参必须用 'params' ,其他的都会报错, 'param'也会报错

 

RankPage.vue 页面接收 传过来的参数:

data() {
  return {
    id: this.$route.params.id,      //注意是'$route', 不是'$router'
  }   
}

 

另外,需要在 App.vue 中添加 <router-view></router-view> 才可以显示路由跳转页面。

 

2、前端布局:

 

实现页面左上角的 返回上一界面按钮 ,该按钮位置相对于浏览器窗口固定,不随屏幕滑动而变动, 显然,需要用到 "position: fixed" ,这里需要注意和 "position: absolute" 的区别, absolute 定位设置父元素为非static(默认)定位,如 relative,再设置 top、 left,其元素的位置就会相对 父元素定位,而 fixed定位特殊,其定位只相对于浏览器的当前视窗,与父元素 是否设置非static定位无关。

当需要调节 fixed 定位的元素位置时, 使用 margin 或 padding,若直接使用 top 或 left 会导致意外的结果。

 

元素居中:

      

左侧图片是我们想要实现的 按钮效果,但是 html 代码运行的却是右侧图片的效果,播放按钮 在水平和垂直方向都不居中,很简单,对图片的父元素用 display: flex 实现:

// html
<div class="play-button">
  <img src="../assets/icon-play.png">   //播放按钮图片
</div>
//CSS
.play-button {
    display: flex;
    justify-content: center;  //水平居中
    align-items: center;      //垂直居中
}

 

移动端1px边框过粗:

看对比图:

                

同样是设置 border: 1px solid ,细看就会发现图一比图二的边框更细,也更美观、精致,放大以后对比更明显,图三和图四肉眼可见,这个问题的原因,需要明确两个概念:逻辑像素和物理像素。

简单来说,CSS 设置的像素为 逻辑像素, 物理像素为手机屏幕上的 实实在在的发光点。最开始,两个像素的比例为1:1,后来随着手机屏幕像素的增加,比例变为1:2,即两个物理像素 表示一个逻辑像素,这就导致了 CSS设置 1px,而到了手机屏幕,就显示为2px的效果。

处理这个问题很简单,只需要在CSS添加代码:

@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5) {
    .border-1px:after, .border-1px:before {
      -webkit-transform: scaleY(.7);
      -webkit-transform-origin: 0 0;
      transform: scaleY(.7);
    }

    .border-1px:after {
      -webkit-transform-origin: left bottom;
    }
  }

  @media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
    .border-1px:after, .border-1px:before {
      -webkit-transform: scaleY(.5);
      transform: scaleY(.5);
    }
  }

 

设置渐变色&判断背景是否为深色:

computed: {                         //vue 计算属性
  color: function () {              //color 方法 将接口返回的数据 "this.topListData.color" 解析为16进制颜色代码
    if (this.topListData !== null) {
      var fixed = '00000' + this.topListData.color.toString(16)
        return '#' + fixed.substr(fixed.length - 6)
    } else {
      return '#ffffff'
    }
  },
  gradientcolor: function () {     //设置颜色颜色 linear-gradient 从top开始渐变
    return '-webkit-linear-gradient(top, rgba(' + this.r + ',' + this.g + ',' + this.b + ', 0), ' + this.color + ')'
  },
  isDark: function () {            //判断是否为深色
    var grayLevel = this.r * 0.299 + this.g * 0.587 + this.b * 0.114
    return (grayLevel < 192)       //若 grayLevel < 192, 返回 true,否则返回 fslae
  },
  r: function () {                 //获取 r 两位颜色代码
    return parseInt(this.color.slice(1, 3), 16)
  },
  g: function () {
    return parseInt(this.color.slice(3, 5), 16)
  },
  b: function () {
    return parseInt(this.color.slice(5, 7), 16)
  }
}

 

3、 点击按钮 弹出 weui 的 ActionSheet :

 

首先需要安装 weui ,

npm install weui

 

默认安装的是最新版本,现在是 weui 2.3.0,注意新旧版本的 css文件中 定义的类名会有不同,比如,2.3.0版本,弹出的菜单类名为 “weui-actionsheet__menu” ,而老版本为: “weui-actionsheet-menu”。

 

安装完毕后,我们在项目根目录下的 node_modules 文件夹下,找到 weui,这里不得不说 weui 团队有点坑的地方,开源的 weui 竟然没有开发手册。

我们在运用的时候,首先要看懂组件的 CSS 代码,所有的 CSS 代码 在 '.\weui\src\style\widget' 里,看懂每一个类名对应的 CSS 效果,这样运用起来就得心应手了。

.\weui\src\example 里是所有的组件demo,查看组件的 html 结构和 DOM 的类名 然后根据自己的需要, 粘贴过来需要用到的 DOM 类名即可。

 

因为我们已经在 main.js  里 import weui, 因此不需要再引入css文件, 这里我们针对 actionsheet 的 css 文件中的重点,进行分析(文件位置: .\weui\src\style\widget\weui-tips\weui-actionsheet.less)

.weui-actionsheet {
  position: fixed;
  left: 0;
  bottom: 0;                           //与下一行结合着看
  transform: translate(0, 100%);       //沿Y轴向下移动一倍,原本bottom:0,再向下移动则 实现隐藏效果
  transition: tranform 0.3s;           //由隐藏到显示的0.3s过渡 动画效果
}
.weui-actionsheet_toggle {
    transform: translate(0, 0);       //位移到 bottom 0,实现 actionsheet 的显示}

 

ActionSheet中菜单的内容,通过 this.$store.dispatch('notifyActionSheet',{ ... })  传递到store:

showMenu: function(index) {              
  this.menuedIndex = index
  let that = this
  this.$store.dispatch('notifyActionSheet',{           //荷载方式分发到 actions
    menus: {                                           //发送数据一: menus对象
      'title.noop': this.topListData.songlist[index].data.songorig + '<br/><span style="color:#666;font-size:12px;">' +this.getSingerStr(this.topListData.songlist[index].data.singer) + '</span>',
      playAsNext : '下一曲播放' ,
      addToPlayList : '添加到播放列表' ,
    },
    handler: {                                         //发送数据二: handler,注意:handler 的函数名 和 menus 里的属性名完全一致,为下一步在actions里,添加点击菜单的事件处理
      ['cancle']() {
      },
      ['playAsNext'](){
        that.$store.commit('addToPlayListAsNextPlay', { //提交到 matutions
          id: that.topListData.songlist[that.menuedIndex].data.songid,
          mid: that.topListData.songlist[that.menuedIndex].data.songmid,
          name: that.topListData.songlist[that.menuedIndex].data.songorig,
          singer: that.topListData.songlist[that.menuedIndex].data.singer,
          albummid: that.topListData.songlist[that.menuedIndex].data.albummid
        })
      },
      ['addToPlayList'](){
        that.$store.commit('addToPlayList', {         //提交到 matutions
          id: that.topListData.songlist[that.menuedIndex].data.songid,
          mid: that.topListData.songlist[that.menuedIndex].data.songmid,
          name: that.topListData.songlist[that.menuedIndex].data.songorig,
          singer: that.topListData.songlist[that.menuedIndex].data.singer,
          albummid: that.topListData.songlist[that.menuedIndex].data.albummid
        })
      }
    }
  })
},

 

actions 再提交到 mutations,mutations 更改 state.menus 。

state: {
  actionSheet: {
    show: false,
    menus: [],
    subject: null
  },
  playingList: {
    show: false
  }
},
getters: {
  getShow(state) {
    return state.actionSheet.show
  },
  getMenus(state) {
    return state.actionSheet.menus
  }
},
actions: {
  notifyActionSheet ({state,commit}, options) {
    commit('showActionSheet', options.menus)
  }
},
mutations: {
  showActionSheet(state, menus){
    state.actionSheet.menus = menus
    state.actionSheet.show = true
  },
  closeActionSheet(state) {
    state.actionSheet.show = false;
  }
}

 

然后在 ActionSheet.vue 里, 添加计算属性,就可以通过 store 的 getters 获取到刚刚添加的菜单:

computed: {
  show() {
    return this.$store.getters.getShow;
  },
  menus() {
    return this.$store.getters.getMenus;
  }
}

 此时,我们的ActionSheet就可以正常显示啦。

 

还剩下最后一步,向ActionSheet.vue 里实现添加菜单响应:

methods:{
      emitEvent(event, menu) {
        if (event === 'on-click-menu' && !/.noop/.test(menu)) {       // /.noop/为 正则表达式语法
          this.$store.dispatch('responceFromActionSheet',menu);     // 将点击的菜单 分发到 responceFromActionSheet 中 AsyncSubject,通过.next() 调用 menu 对应的 handler 
        }
      }
    },

 

好了,RankPage.vue 页面就到这里了,内容有点多,特别是 weui 的 actionsheet 显示自定义菜单并绑定 handler 这里,初次接触可能有点难以理解,慢慢来,多看几遍代码,慢慢理清思路。

 

下一节,我们来研究播放器的播放控制界面,文件位置:src\components\Play.vue.

posted @ 2020-03-27 17:02  几行  阅读(1105)  评论(0编辑  收藏  举报