vuejs开发音乐播放器(一):歌手页面
1、首先看先要实现的功能和界面。2、具体有如下3个。(1)歌手列表:用vuejs中的使用QQ音乐接口,抓取列表,并用better-scroll实现滚动。首先我们去qq音乐官网抓包,使用ajax、jsonp获取接口数据。定义好请求参数和头部。获取到数据后,经过_normalizeSinger函数将Fsinger_mid将歌手名字按英文字母正序排序后输出到新的数组(2)滚动时固定标题栏:通过sing
·
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 = 10
export 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>
更多推荐
已为社区贡献1条内容
所有评论(0)