基于Vue2.0的音乐播放器(2)——歌手模块
1、分析设计稿
顶部:标题
左部:歌手列表
右部:快速入口——A-Z的排序切换
数据:动态获取
2、数据获取及处理
2-1:数据
接口获取——https://y.qq.com/portal/singer_list.html
从控制台network界面上,js对应模块找到
fcg-bin/v8.fcg?channel=singer&page=list&key=all_all_all&pagesize=100&pagenum=1&g_tk=5381&jsonpCallback=GetSingerListCallback&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0
若要使得json格式文件进行可视化处理,需要下载jsonView插件,详情可见博文:
http://blog.csdn.net/zxy9602/article/details/78698169,
安装完插件,并在chrome浏览器上进行加载,可以得到如下效果:
之后再进行配置,进行相关设置,对应console控制台下的Headers目录进行相关查看参数配置
在项目工程api模块定义一个singer.js文件,用于设置与后端交互的接口,从而加载。
由于采用的是jsonp为主载的方式,且加载的第三方都是https://y.qq.com,所以所对应的公共参数相同
import jsonpfrom'common/js/jsonp'
import { commonParams,options }from'./config'
export functiongetSingerList() {
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,
needNewCode:
0,
platform: 'yqq',
g_tk: 1664029744
})
return jsonp(url,data,options)
}
2-2:歌手数据处理
首先,对歌手数据进行处理,其次对数据进行歌手类的封装聚合
2-2.1:获取歌手数据、进行规范化歌手数据、按照/a-zA-Z/进行排序
<scripttype="text/ecmascript-6">
import {getSingerList}from'api/singer'
import {ERR_OK}from'api/config'
import Singerfrom'common/js/singer'
const HOT_NAME ='热门'
const HOT_SINGER_LEN =10
export default {
data() {
return {
// Object
singers: []
}
},
created() {
this._getSingerList()
},
methods: {
// 获取歌手数据
_getSingerList() {
// then()表示promise成功
getSingerList().then((res)=> {
if(res.code ===ERR_OK) {
this.singers =res.data.list
console.log(this._normalizeSinger(this.singers))
}
})
},
// 规范化歌手数据
_normalizeSinger(list) {
// 首先遍历数据
let map = {
// 热门数据
hot: {
title: HOT_NAME,
items: []
}
}
list.forEach((item,index)=> {
if( index <HOT_SINGER_LEN){
// 添加至热门数据
// 由constructor构造器Singer对象,直接引用singer.js
map.hot.items.push(newSinger({
id: item.Fsinger_mid,
name: item.Fsinger_name,
}))
}
// 给list做聚类
const key =item.Findex
if(!map[key]) {
map[key] = {
title: key,
items: []
}
}
map[key].items.push(newSinger({
id: item.Fsinger_mid,
name: item.Fsinger_name
}))
})
// 为了得到有序列表,我们需要处理map
let hot = []
let ret = []
for(letkeyin
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)
}
}
}
</script>
2-2.2:歌手Singer类的封装聚合
由于在map.hot.items()热门模块、map[key].items()对应的A-Z模块,都是将Singer类的配置文件,push()添加到map里边,所以可以将Singer作为一个类进行封装聚合,从而以对象的形式进行调用。
// Singer做聚合,给对应的singer.vue list表单,定义一个Singer类,以对象形式引用
export defaultclassSinger {
constructor({
id, name }) {
this.id =id
this.name =name
this.avatar =`https://y.gtimg.cn/music/photo_new/T002R300x300M000${id}.jpg?max_age=2592000`
}
}
效果展示:
3、左侧——listview.vue电话簿组件
该模块主要用于加载歌手列表界面,由于需要滚动,所以需要Scroll组件的使用
import Scroll from "base/scroll/scroll"
<template>
<scrollclass="listview":data="data">
<ul>
<li
v-for="(group,index)indata"
class="list-group":key="index">
<h2
class="list-group-title">{{group.title}}</h2>
<ul>
<li
v-for="(item,index)ingroup.items"class="list-group-item":key="index">
<img
class="avatar"
:src="item.avatar">
<span
class="name">{{item.name}}</span>
</li>
</ul>
</li>
</ul>
</scroll>
</template>
<scripttype="text/ecmascript-6">
import Scrollfrom'base/scroll/scroll'
export default {
props: {
data: {
type: Array,
default: []
}
},
components: {
Scroll
}
}
</script>
<stylelang="stylus"scoped>
@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>
4、右侧——快速入口的实现(listview.vue)
<!-- 右侧:快速入口/a-zA-Z/ 离开时候需要阻止事件冒泡-->
<div
class="list-shortcut"
@touchstart="onShortcutTouchStart"
@touchmove.stop.prevent="onShortcutTouchMove">
<ul>
<li
v-for="(item,
index) in shortcutList"
class="item"
:data-index="index"
:key="item.key"
:class="{'current' :
currentIndex===index}"
>
{{item}}
</li>
</ul>
</div>
<div
class="list-fixed"
v-show="fixedTitle"
ref="fixed">
<h1
class="fixed-title">{{fixedTitle}}</h1>
</div>
<div
v-show="!data.length"
class="loading-container">
<loading></loading>
</div>
实现流程:
(1)首先,定义锚点个数,即抓取到“A-Z”歌手字母开头总数“18”
(2)其次,定义滚动scroll的歌手group的数据data,以及将要触发的位置
(3)定义两个事件,手指触摸开始+离开,同时设置每次均是“滚动标题”至上原则
(4)通过计算高度变化,以及定义变量diff高度差的方式,来设置元素的偏移
(5)以“当前触摸元素索引”currentIndex,来设置左边“歌手列表”与右边字母索引“a-z”之间的匹配,完成联动的效果。
运行效果图如下:
const ANCHOR_HEIGHT =
18
const TITLE_HEIGHT =
30
export default {
created() {
// 创建一个touch空对象
this.touch = {}
// 创建一个监听scroll事件
this.listenScroll =
true
this.listHeight = []
this.probeType =
3
},
data() {
return {
scrollY: -1,
// 当前滚动到的位置
currentIndex:
0,
// 滚动的上限与下限的滚动差
diff: -1
}
},
props: {
data: {
type: Array,
default: []
}
},
computed: {
// 右侧快速入口
shortcutList() {
return this.data.map((group)
=> {
return group.title.substr(0,
1)
})
},
// 滚动标题至上
fixedTitle() {
if(this.scrollY >
0) return
return this.data[this.currentIndex] ?
this.data[this.currentIndex].title :
''
}
},
methods: {
onShortcutTouchStart(e) {
// 获取当前触摸的index
let anchorIndex =
getData(e.target,
'index')
// 第一次触发时的位置
let firstTouch =
e.touches[0]
// 获取touch到的垂直方向位置
this.touch.y1 =
firstTouch.pageY
// 记录下来需要锚点的index
this.touch.anchorIndex =
anchorIndex
// 引用listview元素,进行滚动
this._scrollTo(anchorIndex)
},
// 触发离开
onShortcutTouchMove(e) {
let firstTouch =
e.touches[0]
this.touch.y2 =
firstTouch.pageY
// 定义需要滚动对少个data元素,|0表示取整,类似于Math.floor()
let delta = (this.touch.y2 -
this.touch.y1) /
ANCHOR_HEIGHT | 0
// 离开move时候的anchorIndex,由于this.touch.anchorIndex为字符串类型,因此要转换为整型int
let anchorIndex =
parseInt(this.touch.anchorIndex) +
delta
this._scrollTo(anchorIndex)
},
// scroll()
scroll(pos) {
// 实时观测滚动到y轴的距离
this.scrollY =
pos.y
},
// 滚动到哪个索引的元素的位置
_scrollTo(index) {
// 若index === null,返回null
if(!index &&
index !== 0){
return
}
// 若index<0 || index> this.listHeight-2
if(index <
0) {
index = 0
} else if(index >
this.listHeight.length -2) {
index = this.listHeight.length -2
}
// 手动设置scrollY的位置
this.scrollY = -this.listHeight[index]
this.$refs.listview.scrollToElement(this.$refs.listGroup[index],
0)
},
// 计算高度
_calculateHeight() {
this.listHeight = []
const list =
this.$refs.listGroup
let height =
0
this.listHeight.push(height)
for(let
i=0;
i<list.length;
i++) {
let item =
list[i]
height += item.clientHeight
this.listHeight.push(height)
}
}
},
watch: {
// 监听data发生变化
data() {
setTimeout(()
=> {
this._calculateHeight()
}, 20)
},
// 监听scrollY的变化
scrollY(newY) {
const listHeight =
this.listHeight
// 当滚动到顶部,newY>0
if(newY >
0) {
this.currentIndex =
0
return
}
// 当中间部分滚动,
for(let
i=0;
i<listHeight.length -1;
i++) {
let height1 =
listHeight[i]
let height2 =
listHeight[i+1]
// 向上滚动srcollY的值为负 所以加上负号
// 若不是height2下限,且在height1与height2之间
if(-newY >=
height1 && -newY<height2) {
this.currentIndex =
i
// 设置diff
this.diff =
height2 + newY
// console.log(this.diff)
// console.log(this.currentIndex)
return
}
}
// 当滚动到底部,且-newY大于最后一个元素的上限
this.currentIndex =
listHeight.length -2
},
// 实时变化的newVal
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
}
}
以上是 基于Vue2.0的音乐播放器(2)——歌手模块 的全部内容, 来源链接: utcz.com/z/375223.html