美文网首页
06.vue.2.X开发音乐App-歌手页面

06.vue.2.X开发音乐App-歌手页面

作者: Ching_Lee | 来源:发表于2018-06-08 10:27 被阅读0次

    github:https://github.com/Ching-Lee/vue-music

    1.分析后台数据

    从QQ音乐网页版获取后台数据




    这里的回掉函数是callback

    • 创建歌手页面的请求文件


    import jsonp from '../assets/js/jsonp'
    import {commonParams, optionsPc} from './config'
    
    export default function getSingerList () {
      const url = 'https://u.y.qq.com/cgi-bin/musicu.fcg'
      const data = {
        'comm': {
          'ct': 24,
          'cv': 10000
        },
        'singerList': {
          'module': 'Music.SingerListServer',
          'method': 'get_singer_list',
          'param': {
            'area': -100,
            'sex': -100,
            'genre': -100,
            'index': -100,
            'sin': 0,
            'cur_page': 1
          }
        }
      }
      // 实现将多个对象拷贝到同一个对象中
      const param = Object.assign({}, commonParams,
        {
          loginUin: 0,
          hostUin: 0,
          format: 'jsonp',
          platform: 'yqq',
          needNewCode: 0,
          data: JSON.stringify(data)
        })
      // 返回值就是promise
      return jsonp(url, param, optionsPc)
    }
    
    
    • 在config.js中一些参数做了改变
    // 配置通用参数
    export const commonParams = {
      g_tk: 5381,
      inCharset: 'utf-8',
      outCharset: 'utf-8',
      notice: 0
    }
    
    // 配置jsonp库的通用的options
    export const options = {
      // 通过qq得到了回掉函数的参数名
      param: 'jsonpCallback'
    }
    
    // PC端的回掉函数
    export const optionsPc = {
      // 通过qq得到了回掉函数的参数名
      param: 'callback'
    }
    
    • singer.vue组件中获取数据
    <script type="text/ecmascript-6">
    import getSingerList from '../../api/singer'
    export default {
      data () {
        return {
          singerlist: []
        }
      },
      created () {
        this._getSingerList()
      },
      methods: {
        _getSingerList () {
          getSingerList().then((result) => {
            this.singerlist = result.singerList.data.singerlist
          }, (err) => { console.log(err) }
          )
        }
      }
    }
    

    2.我们将得到的数据根据country聚类

    要实现的效果
    • singer.vue中添加方法
      _singerCountryMap () {
        // 将数据按照地点区分
          let map = {}
          for (let value of this.singerlist) {
            let key = value.country
            if (!map[key]) {
              let item = []
              map[key] = item
            }
            map[key].push(new Singer(value))
          }
          return map
        }
      }
    
    • Singer类里面存储了和歌手相关的信息,图片的地址是根据singer_mid得到的


    export default class Singer {
      constructor (value) {
        this.country = value.country
        this.singer_id = value.singer_id
        this.name = value.singer_name
        this.singer_pic = 'http://y.gtimg.cn/music/photo_new/T001R150x150M000' + value.singer_mid + '.jpg?max_age=2592000'
      }
    }
    

    3.创建listView组件


    遍历data对象,对于每一个城市的键值对是一个li
    然后在li中又嵌套遍历该城市的value值(是该城市的歌手的数组)。

    <template>
    <ul>
      <li v-for="(value, key, index) in data" v-bind:key="index">
        <h2 class="title">{{key}}</h2>
        <ul>
          <li v-for="(item, index) in (value)" v-bind:key="index" class="singer_item">
            <img v-bind:src="item.singer_pic" class="singerPic">
            <span class="singer_name">{{item.name}}</span>
          </li>
        </ul>
      </li>
    </ul>
    </template>
    
    <script type="text/ecmascript-6">
    export default {
      props: {
        data: Object,
        default: null
      }
    }
    </script>
    
    <style>
      .title{
        height: 2rem;
        background-color: darkorange;
        color: whitesmoke;
        padding: 0.25rem 1rem;
        line-height: 2rem;
        margin-bottom: 0.5rem;
      }
      .singerPic{
        width: 5rem;
        border-radius: 50%;
      }
      .singer_item{
        padding: 0.5rem 1rem;
        position:relative;
      }
      .singer_name{
       color: white;
        margin-left: 2rem;
        position: absolute;
        bottom: 50%;
        transform: translate(0,50%);
      }
    </style>
    
    
    • 在singer.vue中调用该组件
    <template>
      <div class="singer">
        <listview v-if="singerlist.length" v-bind:data=" _singerCountryMap ()"></listview>
      </div>
    </template>
    
    <style>
     .singer{
       background-color: orange;
     }
    </style>
    

    3.图片懒加载

    我们现在是一次性加载所有的图片,会影响性能,这里应该使用图片懒加载
    安装vue-lazyload插件



    在main.js中引入vue.lazyload

    import VueLazyLoad from 'vue-lazyload'
    Vue.use(VueLazyLoad, {
      loading:require('./assets/images/music_logo.png')
    })
    

    就会进行首屏加载,之后滚动到要显示的地方会再加载。
    在listveiw中更改,使用v-lazy标签

     <ul>
          <li v-for="(item, index) in (value)" v-bind:key="index" class="singer_item">
            <img v-lazy="item.singer_pic" class="singerPic">
            <span class="singer_name">{{item.name}}</span>
          </li>
        </ul>
    

    4.正在载入loading组件


    <template>
      <div class="loading">
        <img src="./loading.gif">
        <p class="dec">{{title}}</p>
      </div>
    
    </template>
    
    <script type="text/ecmascript-6">
    export default {
      props: {
        title: {
          type: String,
          default: '正在载入...'
        }
      }
    }
    </script>
    
    <style>
    .loading{
      position: absolute;
      top:50%;
      left:50%;
      transform: translate(-50%,-50%);
      text-align: center;
    }
    .loading p{
      font-size: 14px;
    }
    
    </style>
    
    

    在歌手组件中调用loading组件,使用v-show,在列表没有长度的时候显示,有长度不显示

    <template>
      <div>
        <div class="singer" v-if="singerlist.length">
          <listview  v-bind:data=" _singerCountryMap ()"></listview>
        </div>
        <div v-show="!singerlist.length">
          <loading></loading>
        </div>
      </div>
    </template>
    

    5.快速导航入口

    • 首先添加计算属性,获取到所有城市的名称:
    computed: {
        shortcutList () {
          let keylist = []
          for (let key in this.data) {
            if (key) {
              keylist.push(key)
            }
          }
          return keylist
        }
      },
    
    • 在template中添加快速入口的div
     <div v-if="data">
          <ul ref="quickNav" class="shortpart" @click="onShortcutTouchStart">
            <li v-for="(key,index) in shortcutList" v-bind:key="index" class="shortitem" v-bind:data-index="index">
              {{key}}
            </li>
          </ul>
        </div>
    
    mounted () {
        this.$nextTick(function () {
          this.citylist = this.$refs.roll.children
          this.headerHeight = this.citylist[0].offsetTop
          this.quicknavlist = this.$refs.quickNav.children
          this.scrollListener()
        })
      },
    

    整个ul使用了固定定位。

    <style>
      .title{
        height: 2rem;
        background-color: darkorange;
        color: whitesmoke;
        padding: 0.25rem 1rem;
        line-height: 2rem;
        margin-bottom: 0.5rem;
      }
      .singerPic{
        width: 5rem;
        border-radius: 50%;
      }
      .singer_item{
        padding: 0.5rem 1rem;
        position:relative;
      }
      .singer_name{
       color: white;
        margin-left: 2rem;
        position: absolute;
        bottom: 50%;
        transform: translate(0,50%);
      }
      .shortpart{
        position: fixed;
        top:50%;
        right:0;
        transform: translate(0,-25%);
        width:4rem;
        text-align: center;
      }
      .shortitem{
        margin: 1rem 0;
        color: black;
        font-size: 12px;
      }
    </style>
    
    • 可以看到给ul注册了点击事件,使用了事件委托的原理。
    • scrollTo的意思就是把传入参数的x,y坐标移动到浏览器(0,0)点。
    • offsetLeft 和 offsetTop 返回的是相对于 offsetParent 元素的距离,而 offsetParent 指的是一个元素最近的父级定位元素,如果没有定位元素就是文档根节点。所以会超出视窗

    点击了之后通过event.target拿到被点击元素的li,获取到data-index属性,然后去遍历左边的城市大的li,如果这个li的索引和data-index相同,就去计算出当前这个li距离可视窗口顶部的距离,然后减去头部和导航栏的距离,就是让这个计算出的高度滚动到(0,0)点。

    methods: {
        onShortcutTouchStart (event) {
          // 点击的快速入口的li
          let current = event.target
          // 拿到点击的索引
          let index = current.getAttribute('data-index')
          // 遍历各个城市的li(每个li里面嵌套了title和ul(里面是该城市的歌手))
          for (let liIndex in this.citylist) {
            // 如果点击的这个快速入口的索引和
            if (liIndex === index) {
              let height = this.citylist[liIndex].offsetTop - this.headerHeight
              window.scrollTo(0, height)
            }
          }
        },
    
    • 之后我们添加一个滚动监听事件,看各个城市的标题出现在屏幕中,我们就让快速导航栏颜色变白,同时他的前一个或者后一个如果是白的,就让他变黑并break。
      这里用到了事件节流
     scrollListener () {
          let _self = this
          let timeout
          window.addEventListener('scroll', function () {
            if (timeout) {
              clearTimeout(timeout)
            }
            // 事件节流
            timeout = setTimeout(callback(_self), 100)
          })
          function callback (_self) {
            for (let index = 0; index < _self.citylist.length; index++) {
              // 把标题那一行给拿出来,h2
              let title = _self.citylist[index].getElementsByTagName('h2')[0]
              let titleTop = title.offsetTop - (document.body.scrollTop || document.documentElement.scrollTop)
              let currentli = _self.quicknavlist[index]
              if (currentli) {
                if (titleTop >= _self.headerHeight && titleTop <= document.documentElement.clientHeight) {
                  currentli.style.color = 'white'
                  if (_self.quicknavlist[index - 1].style.color === 'white') {
                    _self.quicknavlist[index - 1].style.color = 'black'
                  }
                  if (_self.quicknavlist[index + 1].style.color === 'white') {
                    _self.quicknavlist[index + 1].style.color = 'black'
                  }
                  break
                }
              }
            }
          }
        },
    

    相关文章

      网友评论

          本文标题:06.vue.2.X开发音乐App-歌手页面

          本文链接:https://www.haomeiwen.com/subject/kqymsftx.html