vuejs开发音乐播放器(一):歌手页面

来源:互联网 发布:网络硬盘录像机的用途 编辑:程序博客网 时间:2024/05/17 01:43

1、首先看先要实现的功能和界面。

这里写图片描述

2、具体有如下3个。

(1)歌手列表:用vuejs中的使用QQ音乐接口,抓取列表,并用better-scroll实现滚动。首先我们去qq音乐官网抓包,使用ajax、jsonp获取接口数据。定义好请求参数和头部。获取到数据后,经过_normalizeSinger函数将Fsinger_mid将歌手名字按英文字母正序排序后输出到新的数组
(2)滚动时固定标题栏:通过singer.vue中ES6代码实现。
(3)右侧固定索引:click点击定位,touchmove滑动定位,锚点定位,高亮显示,具体看listview.vue的代码

3、具体代码实现如下。由于篇幅有限,只贴上重要的部分,有想了解的可以加我好友探讨问题。

1、jsonp.js

export default function jsonp(url, data, option) {  url += (url.indexOf('?') < 0 ? '?' : '&') + param(data)  return new Promise((resolve, reject) => {    originJSONP(url, option, (err, data) => {      if (!err) {        resolve(data)      } else {        reject(err)      }    })  })}

2、config.js

export const commonParams = {  g_tk: 5381,  inCharset: 'utf-8',  outCharset: 'utf-8',  notice: 0,  format: 'jsonp'}export const options = {  param: 'jsonpCallback'}export const ERR_OK = 0

3、singer.js(引入前两个文件)

import jsonp from 'common/js/jsonp'import {commonParams, options} from './config'export function getSingerList() {  const url = 'https://c.y.qq.com/v8/fcg-bin/v8.fcg'  const data = Object.assign({}, commonParams, {    channel: 'singer',    page: 'list',    key: 'all_all_all',    pagesize: 100,    pagenum: 1,    hostUin: 0,    loginUin: 0,    needNewCode: 0,    platform: 'yqq',    g_tk: 5381,    inCharset: 'utf8',    outCharset: 'utf-8',    notice: 0,    format: 'jsonp'  })  return jsonp(url, data, options)}

4、scroll.vue

<template>  <div ref="wrapper">  <slot></slot>  </div></template><script type="text/ecmascript-6">  import BScroll from 'better-scroll'  export default {    props: {      probeType: {        type: Number,        default: 1      },      click: {        type: Boolean,        default: true      },      data: {        type: Array,        default: null      },      listenScroll: {        type: Boolean,        default: false      }    },    mounted() {      setTimeout(() => {        this._initScroll()      }, 20)      window.addEventListener('resize', () => {        if (!this.slider) {          return        }        this._setSliderWidth(true)        this.slider.refresh()      })    },    methods: {      _initScroll() {        if (!this.$refs.wrapper) {          return        }        this.scroll = new BScroll(this.$refs.wrapper, {          probeType: this.probeType,          click: this.click        })        if (this.listenScroll) {          let me = this          this.scroll.on('scroll', (pos) => {            me.$emit('scroll', pos)          })        }      },      enable() {        this.scroll && this.scroll.enable()      },      disable() {        this.scroll && this.scroll.disable()      },      refresh() {        this.scroll && this.scroll.refresh()      },      scrollTo() {        this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)      },      scrollToElement() {        this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)      }    },    watch: {      data() {        setTimeout(() => {          this.refresh()        }, 20)      }    }  }</script><style scoped lang="stylus" rel="stylesheet/stylus"></style>

5、listview.vue

<template>  <scroll          ref="listview"          class="listview"          :data="data"          :listenScroll="listenScroll"          @scroll="scroll"          :probeType="probeType"  >  <ul>    <li v-for="group in data" class="list-group" ref="listGroup">      <h2 class="list-group-title">{{group.title}}</h2>      <uL>        <li v-for="item in group.items" class="list-group-item">          <img class="avatar" v-lazy="item.avatar">          <span class="name">{{item.name}}</span>        </li>      </uL>    </li>  </ul>    <div class="list-shortcut" @touchstart="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove">      <ul>        <li                v-for="(item,index) in shortcutList"                class="item"                :class="{'current':currentIndex===index}"                :data-index="index"        >          {{item}}        </li>      </ul>    </div>    <div class="list-fixed" v-show="fixedTitle" ref="fixed">      <h1 class="fixed-title">        {{fixedTitle}}      </h1>    </div>    <div class="loading-content" v-show="!data.length">      <loading></loading>    </div>  </scroll></template><script type="text/ecmascript-6">  import Loading from 'base/loading/loading'  import Scroll from 'base/scroll/scroll'  const ANCHOR_HEIGHT = 18  const TITLE_HEIGHT = 30  export default {    props: {      data: {        type: Array,        default: []      }    },    created() {      this.touch = {}      this.listenScroll = true      this.listenHeight = []      this.probeType = 3    },    data() {      return {        scrollY: -1,        currentIndex: 0,        diff: -1      }    },    computed: {      shortcutList() {        return this.data.map((group) => {          return group.title.substring(0, 1)        })      },      fixedTitle() {        if (this.scrollY > 0) {          return ''        }        return this.data[this.currentIndex] ? this.data[this.currentIndex].title : ''      }    },    methods: {      getData(el, name, val) {        const prefix = 'data-'        if (val) {          return el.setAttribute(prefix + name, val)        }        return el.getAttribute(prefix + name)      },      onShortcutTouchStart(e) {        let anchorIndex = this.getData(e.target, 'index')        let firstTouch = e.touches[0]        this.touch.y1 = firstTouch.pageY        this.touch.anchorIndex = anchorIndex        this._scrollTo(anchorIndex)      },      onShortcutTouchMove(e) {        let firstTouch = e.touches[0]        this.touch.y2 = firstTouch.pageY        let delta = (this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT | 0        let anchorIndex = parseInt(this.touch.anchorIndex) + delta        this._scrollTo(anchorIndex)      },      scroll(pos) {        this.scrollY = pos.y      },      _scrollTo(index) {        console.log(index)        if (index === null) {          return        }        if (index < 0) {          index = 0        } else if (index > this.listenHeight - 2) {          index = this.listenHeight - 2        }        this.scrollY = -this.listenHeight[index]        this.$refs.listview.scrollToElement(this.$refs.listGroup[index], 0)      },      _calculateHeight() {        this.listenHeight = []        const list = this.$refs.listGroup        let height = 0        this.listenHeight.push(height)        for (let i = 0; i < list.length; i++) {          let item = list[i]          height += item.clientHeight          this.listenHeight.push(height)        }      }    },    watch: {      data() {        setTimeout(() => {          this._calculateHeight()        }, 20)      },      scrollY(newY) {        const listenHeight = this.listenHeight        // when the top newY > 0        if (newY > 0) {          this.currentIndex = 0          return        }        //  when the middle        for (let i = 0; i < listenHeight.length - 1; i++) {          let height1 = listenHeight[i]          let height2 = listenHeight[i + 1]          if (!height2 || (-newY >= height1 && -newY < height2)) {            this.currentIndex = i            this.diff = height2 + newY            console.log(this.currentIndex)            return          }        }        //  when the middle        this.currentIndex = this.listenHeight - 2      },      diff(newVal) {        let fixedTop = (newVal > 0 && newVal < TITLE_HEIGHT ? newVal - TITLE_HEIGHT : 0)        if (this.fixedTop === fixedTop) {          return        }        this.fixedTop = fixedTop        this.$refs.fixed.style.transform = 'translate3d(0,' + fixedTop + 'px,0)'      }    },    components: {      Scroll,      Loading    }  }</script><style scoped lang="stylus" rel="stylesheet/stylus">  @import "~common/stylus/variable"  .listview    position: relative    width: 100%    height: 100%    overflow: hidden    background: $color-background    .list-group      padding-bottom: 30px      .list-group-title        height: 30px        line-height: 30px        padding-left: 20px        font-size: $font-size-small        color: $color-text-l        background: $color-highlight-background      .list-group-item        display: flex        align-items: center        padding: 20px 0 0 30px        .avatar          width: 50px          height: 50px          border-radius: 50%        .name          margin-left: 20px          color: $color-text-l          font-size: $font-size-medium    .list-shortcut      position: absolute      z-index: 30      right: 0      top: 50%      transform: translateY(-50%)      width: 20px      padding: 20px 0      border-radius: 10px      text-align: center      background: $color-background-d      font-family: Helvetica      .item        padding: 3px        line-height: 1        color: $color-text-l        font-size: $font-size-small        &.current          color: $color-theme    .list-fixed      position: absolute      top: 0      left: 0      width: 100%      .fixed-title        height: 30px        line-height: 30px        padding-left: 20px        font-size: $font-size-small        color: $color-text-l        background: $color-highlight-background    .loading-container      position: absolute      width: 100%      top: 50%      transform: translateY(-50%)</style>

6、singer.vue

<template>  <div class="singer">    <listview :data="singers"></listview>  </div></template><script type="text/ecmascript-6">import {getSingerList} from 'api/singer'import {ERR_OK} from 'api/config'import Singer from 'common/js/singer'import Listview from 'base/listview/listview'const HOT_NAME = '热门'const HOT_SINGER_LEN = 10export default {  data() {    return {      singers: []    }  },  created() {    this._getSingerList()  },  methods: {    _getSingerList() {      getSingerList().then((res) => {        if (res.code === ERR_OK) {          this.singers = this._normalizeSinger(res.data.list)          console.log(this.singers)        }      })    },    _normalizeSinger(list) {      let map = {        hot: {          title: HOT_NAME,          items: []        }      }      list.forEach((item, index) => {        if (index < HOT_SINGER_LEN) {          map.hot.items.push(new Singer({            id: item.Fsinger_mid,            name: item.Fsinger_name          }))        }        const key = item.Findex        if (!map[key]) {          map[key] = {            title: key,            items: []          }        }        map[key].items.push(new Singer({          id: item.Fsinger_mid,          name: item.Fsinger_name        }))      })      let hot = []      let ret = []      for (let key in map) {        let val = map[key]        if (val.title.match(/[a-zA-Z]/)) {          ret.push(val)        } else if (val.title === HOT_NAME) {          hot.push(val)        }      }      ret.sort((a, b) => {        return a.title.charCodeAt(0) - b.title.charCodeAt(0)      })      return hot.concat(ret)    }  },  components: {    Listview,    Singer  }}</script><style scoped lang="stylus" rel="stylesheet/stylus">  .singer    position: fixed    top: 88px    bottom: 0    width: 100%</style>
原创粉丝点击