06.vue.2.X開發(fā)音樂App-歌手頁面

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

1.分析后臺數(shù)據(jù)

從QQ音樂網(wǎng)頁版獲取后臺數(shù)據(jù)




這里的回掉函數(shù)是callback

  • 創(chuàng)建歌手頁面的請求文件


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
      }
    }
  }
  // 實現(xiàn)將多個對象拷貝到同一個對象中
  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中一些參數(shù)做了改變
// 配置通用參數(shù)
export const commonParams = {
  g_tk: 5381,
  inCharset: 'utf-8',
  outCharset: 'utf-8',
  notice: 0
}

// 配置jsonp庫的通用的options
export const options = {
  // 通過qq得到了回掉函數(shù)的參數(shù)名
  param: 'jsonpCallback'
}

// PC端的回掉函數(shù)
export const optionsPc = {
  // 通過qq得到了回掉函數(shù)的參數(shù)名
  param: 'callback'
}
  • singer.vue組件中獲取數(shù)據(jù)
<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.我們將得到的數(shù)據(jù)根據(jù)country聚類

要實現(xiàn)的效果
  • singer.vue中添加方法
  _singerCountryMap () {
    // 將數(shù)據(jù)按照地點區(qū)分
      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類里面存儲了和歌手相關(guān)的信息,圖片的地址是根據(jù)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.創(chuàng)建listView組件


遍歷data對象,對于每一個城市的鍵值對是一個li
然后在li中又嵌套遍歷該城市的value值(是該城市的歌手的數(shù)組)。

<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中調(diào)用該組件
<template>
  <div class="singer">
    <listview v-if="singerlist.length" v-bind:data=" _singerCountryMap ()"></listview>
  </div>
</template>
<style>
 .singer{
   background-color: orange;
 }
</style>

3.圖片懶加載

我們現(xiàn)在是一次性加載所有的圖片,會影響性能,這里應(yīng)該使用圖片懶加載
安裝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>

在歌手組件中調(diào)用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.快速導(dǎo)航入口

  • 首先添加計算屬性,獲取到所有城市的名稱:
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的意思就是把傳入?yún)?shù)的x,y坐標移動到瀏覽器(0,0)點。
  • offsetLeft 和 offsetTop 返回的是相對于 offsetParent 元素的距離,而 offsetParent 指的是一個元素最近的父級定位元素,如果沒有定位元素就是文檔根節(jié)點。所以會超出視窗

點擊了之后通過event.target拿到被點擊元素的li,獲取到data-index屬性,然后去遍歷左邊的城市大的li,如果這個li的索引和data-index相同,就去計算出當前這個li距離可視窗口頂部的距離,然后減去頭部和導(dǎo)航欄的距離,就是讓這個計算出的高度滾動到(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)
        }
      }
    },
  • 之后我們添加一個滾動監(jiān)聽事件,看各個城市的標題出現(xiàn)在屏幕中,我們就讓快速導(dǎo)航欄顏色變白,同時他的前一個或者后一個如果是白的,就讓他變黑并break。
    這里用到了事件節(jié)流
 scrollListener () {
      let _self = this
      let timeout
      window.addEventListener('scroll', function () {
        if (timeout) {
          clearTimeout(timeout)
        }
        // 事件節(jié)流
        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
            }
          }
        }
      }
    },
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容