內容均爲《Vue2.0開發企業級移動端音樂Web App 》學習筆記javascript
包括歌手數據的抓取和處理、Singer 類的封裝、類通信錄組件 listview開發和應用。html
作一個相似於通信錄的功能,當滑動左邊的列表,右側的相應的字母分類加上選中的樣式,當點擊右側的字母的時候,滑動到左側相應的項,頭部標題固定。 咱們但願獲得的數據是vue
src\api\singer.jsjava
import {commonParams,options} from './config'
import jsonp from 'common/js/jsonp.js'
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,
needNewCode: 0,
platform: 'yqq',
g_tk: 1664029744,
})
return jsonp(url,data,options)
在singer.vue中請求數據
created(){
this._getsingerList()
},
methods:{
_getsingerList(){
getsingerList().then(res=>{
if(res.code===ERR_OK){
//將數據作過濾,過濾出咱們須要的數據,咱們將前10當是熱門的數據
this._normalizeSinger(res.data.list)
}
}).catch(err=>{
console.log(err);
})
},
複製代碼
獲得的數據以下 json
對歌手數據進行處理api
_normalizeSinger(list){
//首先咱們先對數據進行分類
let map={
hot:{
title: HOT_NAME,
items: []
}
}
//首先咱們先獲得熱門的歌曲,咱們將獲得的數據的前十條做爲熱門數據
list.forEach((item,index) => {
//在這裏咱們獲得了熱門的歌手
if(index<HOT_SINGER_LEN){
map.hot.items.push({
id: item.Fsinger_mid,
name: item.Fsinger_name,
avatar: `https://y.gtimg.cn/music/photo_new/T001R300x300M000${item.Fsinger_mid}.jpg?max_age=2592000`
});
}
//在這裏咱們獲得各個字母的的分類的歌手
let key=item.Findex;
if(!map[key]){
//當沒有當前的字母項,咱們建立一個
map[key]={
title: key,
items: []
}
}
//把相應的項添加到map[key]中
map[key].items.push({
id: item.Fsinger_mid,
name: item.Fsinger_name,
avatar: `https://y.gtimg.cn/music/photo_new/T001R300x300M000${item.Fsinger_mid}.jpg?max_age=2592000`
})
});
}
複製代碼
咱們發如今push歌手信息的時候,這部分的代碼是同樣的,而avatar的計算是根據id計算的,咱們但願構造一個歌手類,經過傳入id和name獲得咱們想要的數據格式。提升代碼的複用性 common\js\singer.js數組
export default class Singer {
constructor({name,id}){
this.name=name,
this.id=id
this.avatar = `https://y.gtimg.cn/music/photo_new/T001R300x300M000${id}.jpg?max_age=2592000`
}
}
複製代碼
作調整bash
//引入歌手類文件
import Singer from 'common/js/singer'
//對歌手數據進行處理
_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,
})
);
}
//在這裏咱們獲得各個字母的的分類的歌手
let key=item.Findex;
if(!map[key]){
//當沒有當前的字母項,咱們建立一個
map[key]={
title: key,
items: []
}
}
//把相應的項添加到map[key]中
map[key].items.push(new Singer({
id: item.Fsinger_mid,
name: item.Fsinger_name,
})
)
});
console.log(map);
}
複製代碼
咱們發現這個順序是雜亂無章的,咱們其實但願的是按照順序排列,而且數據的結構是一個數組,如今咱們來處理一下這些數據。對上面的數據進行排序 熱門>A>B>C...app
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);
複製代碼
在_getsingerList()中獲得ingerListdom
this.singerList=this._normalizeSinger(res.data.list)
複製代碼
獲得ingerList以後,咱們就能夠作滾動列表了,把data傳入到scroll裏面 引入scroll
import scroll from '../scroll/scroll'
複製代碼
<template>
<scroll :data="data" class="listview">
<ul>
<li class="list-group" v-for="(item,index) in data" :key="index">
<h2 class="list-group-title">{{item.title}}</h2>
<ul>
<li class="list-group-item" v-for="singer in item.items" :key="singer.id">
<img class="avatar" v-lazy="singer.avatar">
<span class="name">{{singer.name}}</span>
</li>
</ul>
</li>
</ul>
</scroll>
</template>
複製代碼
佈局
shortcut(){////獲得title的集合數組,‘熱門’取1個字
let arr=[];
this.data.forEach(item => {
arr.push(item.title.substr(0,1));
});
return arr
}
複製代碼
<!-- 右側點點部分-->
<div class="list-shortcut">
<ul>
<li :data-index="index" class="item" v-for="(item,index) in shortcut" :key="index">
{{item}}
</li>
</ul>
</div>
複製代碼
觸摸右側標題 爲了獲得當前觸摸的是哪個點點,咱們給加上了屬性data-index
<div class="list-shortcut">
<ul @touchstart="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove">
<li v-for="(item,index) in shortcut" :key="index" :data-index="index" class="item" :class="{current:index==currentIndex}" >
{{item}}
</li>
</ul>
</div>
複製代碼
當咱們點擊ul的時候咱們須要獲取到當前的點擊元素的data-index來判斷點擊的是哪個元素,那麼怎麼獲取到data-index 這個屬性呢,咱們在dom,.js中export一個getData的方法。來設置或者獲取屬性
//給元素添加屬性或者獲得屬性的值
export function getData(el,attrname,value){
let prefix='data-'
let name=prefix+attrname
if(value){
el.setAttribute(name,value);
}else{
return el.getAttribute(name);
}
}
複製代碼
當開始觸屏的時候記錄當前的y1 和 當前的索引,而且滾動到相應的位置。 在scroll組件中定義兩個方法
scrollTo() {
// 滾動到指定的位置;這裏使用apply 將傳入的參數,傳入到this.scrollTo()
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement(){
//滾動到指定的元素
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
}
複製代碼
在listview的右側字母列表中監聽觸摸事件
onShortcutTouchStart(e) {
//記錄開始的位置
this.touch.y1=e.touches[0].pageY;//開始的y座標
this.touch.anchorIndex=anchorIndex;
//去到listHeigth數組的某一項
this._scollTo(anchorIndex);
},
複製代碼
listHeigth用來存 左側的每個項的 的clientHeight。這樣咱們能夠計算當前的元素索引在左側的哪一個區間。
watch: {
data(){
setTimeout(()=>{
this.listHeigth=[];
let height=0;
this.listHeigth.push(0);
let list=[...this.$refs.listGroup];
for(let i=0;i<list.length;i++){
height+=list[i].clientHeight;
this.listHeigth.push(height);
}
},20)
}
},
//滑動到哪裏
_scollTo(index){
this.$refs.listview.scrollTo(0,-this.listHeigth[index],0);
this.currentIndex=index;
}
複製代碼
當手指滑動的時候,計算y2 和 y1的差值/字母塊的高度,獲得須要偏移多少個字母塊,再加上當前的位置,獲得須要滾動到的index,再滾動到相應的位置。 由於scroll自己能夠滑動,當咱們滑動右側的時候,咱們會阻止默認行爲,阻止事件冒泡
@touchmove.stop.prevent="onShortcutTouchMove"
複製代碼
//計算手機放下的位置,和move以後的位置,比較,讓左邊的滾動,用this.touch來記錄位置信息
onShortcutTouchMove(e){
this.touch.y2=e.touches[0].pageY;//開始的y座標
let delta=(this.touch.y2-this.touch.y1)/CUTHEIGHT | 0//向下取整。獲取到最接近的值
let anchorIndex=parseInt(this.touch.anchorIndex)+delta
this._scollTo(anchorIndex);
},
複製代碼
當咱們滑動左側的時候,咱們須要監聽咱們當前滑動的縱向y的值,來判斷當前滑動的是哪個區間,來肯定應該指向的索引
包括歌手數據的抓取和處理、Singer 類的封裝、類通信錄組件 listview開發和應用。
作一個相似於通信錄的功能,當滑動左邊的列表,右側的相應的字母分類加上選中的樣式,當點擊右側的字母的時候,滑動到左側相應的項,頭部標題固定。 咱們但願獲得的數據是
src\api\singer.js
import {commonParams,options} from './config'
import jsonp from 'common/js/jsonp.js'
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,
needNewCode: 0,
platform: 'yqq',
g_tk: 1664029744,
})
return jsonp(url,data,options)
在singer.vue中請求數據
created(){
this._getsingerList()
},
methods:{
_getsingerList(){
getsingerList().then(res=>{
if(res.code===ERR_OK){
//將數據作過濾,過濾出咱們須要的數據,咱們將前10當是熱門的數據
this._normalizeSinger(res.data.list)
}
}).catch(err=>{
console.log(err);
})
},
複製代碼
獲得的數據以下
對歌手數據進行處理
_normalizeSinger(list){
//首先咱們先對數據進行分類
let map={
hot:{
title: HOT_NAME,
items: []
}
}
//首先咱們先獲得熱門的歌曲,咱們將獲得的數據的前十條做爲熱門數據
list.forEach((item,index) => {
//在這裏咱們獲得了熱門的歌手
if(index<HOT_SINGER_LEN){
map.hot.items.push({
id: item.Fsinger_mid,
name: item.Fsinger_name,
avatar: `https://y.gtimg.cn/music/photo_new/T001R300x300M000${item.Fsinger_mid}.jpg?max_age=2592000`
});
}
//在這裏咱們獲得各個字母的的分類的歌手
let key=item.Findex;
if(!map[key]){
//當沒有當前的字母項,咱們建立一個
map[key]={
title: key,
items: []
}
}
//把相應的項添加到map[key]中
map[key].items.push({
id: item.Fsinger_mid,
name: item.Fsinger_name,
avatar: `https://y.gtimg.cn/music/photo_new/T001R300x300M000${item.Fsinger_mid}.jpg?max_age=2592000`
})
});
}
複製代碼
咱們發如今push歌手信息的時候,這部分的代碼是同樣的,而avatar的計算是根據id計算的,咱們但願構造一個歌手類,經過傳入id和name獲得咱們想要的數據格式。提升代碼的複用性 common\js\singer.js
export default class Singer {
constructor({name,id}){
this.name=name,
this.id=id
this.avatar = `https://y.gtimg.cn/music/photo_new/T001R300x300M000${id}.jpg?max_age=2592000`
}
}
複製代碼
作調整
//引入歌手類文件
import Singer from 'common/js/singer'
//對歌手數據進行處理
_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,
})
);
}
//在這裏咱們獲得各個字母的的分類的歌手
let key=item.Findex;
if(!map[key]){
//當沒有當前的字母項,咱們建立一個
map[key]={
title: key,
items: []
}
}
//把相應的項添加到map[key]中
map[key].items.push(new Singer({
id: item.Fsinger_mid,
name: item.Fsinger_name,
})
)
});
console.log(map);
}
複製代碼
咱們發現這個順序是雜亂無章的,咱們其實但願的是按照順序排列,而且數據的結構是一個數組,如今咱們來處理一下這些數據。對上面的數據進行排序 熱門>A>B>C...
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);
複製代碼
在_getsingerList()中獲得ingerList
this.singerList=this._normalizeSinger(res.data.list)
複製代碼
獲得ingerList以後,咱們就能夠作滾動列表了,把data傳入到scroll裏面 引入scroll
import scroll from '../scroll/scroll'
複製代碼
<template>
<scroll :data="data" class="listview">
<ul>
<li class="list-group" v-for="(item,index) in data" :key="index">
<h2 class="list-group-title">{{item.title}}</h2>
<ul>
<li class="list-group-item" v-for="singer in item.items" :key="singer.id">
<img class="avatar" v-lazy="singer.avatar">
<span class="name">{{singer.name}}</span>
</li>
</ul>
</li>
</ul>
</scroll>
</template>
複製代碼
佈局
shortcut(){////獲得title的集合數組,‘熱門’取1個字
let arr=[];
this.data.forEach(item => {
arr.push(item.title.substr(0,1));
});
return arr
}
複製代碼
<!-- 右側點點部分-->
<div class="list-shortcut">
<ul>
<li :data-index="index" class="item" v-for="(item,index) in shortcut" :key="index">
{{item}}
</li>
</ul>
</div>
複製代碼
觸摸右側標題 爲了獲得當前觸摸的是哪個點點,咱們給加上了屬性data-index
<div class="list-shortcut">
<ul @touchstart="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove">
<li v-for="(item,index) in shortcut" :key="index" :data-index="index" class="item" :class="{current:index==currentIndex}" >
{{item}}
</li>
</ul>
</div>
複製代碼
當咱們點擊ul的時候咱們須要獲取到當前的點擊元素的data-index來判斷點擊的是哪個元素,那麼怎麼獲取到data-index 這個屬性呢,咱們在dom,.js中export一個getData的方法。來設置或者獲取屬性
//給元素添加屬性或者獲得屬性的值
export function getData(el,attrname,value){
let prefix='data-'
let name=prefix+attrname
if(value){
el.setAttribute(name,value);
}else{
return el.getAttribute(name);
}
}
複製代碼
當開始觸屏的時候記錄當前的y1 和 當前的索引,而且滾動到相應的位置。 在scroll組件中定義兩個方法
scrollTo() {
// 滾動到指定的位置;這裏使用apply 將傳入的參數,傳入到this.scrollTo()
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement(){
//滾動到指定的元素
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
}
複製代碼
在listview的右側字母列表中監聽觸摸事件
onShortcutTouchStart(e) {
//記錄開始的位置
this.touch.y1=e.touches[0].pageY;//開始的y座標
this.touch.anchorIndex=anchorIndex;
//去到listHeigth數組的某一項
this._scollTo(anchorIndex);
},
複製代碼
listHeigth用來存 左側的每個項的 的clientHeight。這樣咱們能夠計算當前的元素索引在左側的哪一個區間。
watch: {
data(){
setTimeout(()=>{
this.listHeigth=[];
let height=0;
this.listHeigth.push(0);
let list=[...this.$refs.listGroup];
for(let i=0;i<list.length;i++){
height+=list[i].clientHeight;
this.listHeigth.push(height);
}
},20)
}
},
//滑動到哪裏
_scollTo(index){
this.$refs.listview.scrollTo(0,-this.listHeigth[index],0);
this.currentIndex=index;
}
複製代碼
當手指滑動的時候,計算y2 和 y1的差值/字母塊的高度,獲得須要偏移多少個字母塊,再加上當前的位置,獲得須要滾動到的index,再滾動到相應的位置。 由於scroll自己能夠滑動,當咱們滑動右側的時候,咱們會阻止默認行爲,阻止事件冒泡
@touchmove.stop.prevent="onShortcutTouchMove"
複製代碼
//計算手機放下的位置,和move以後的位置,比較,讓左邊的滾動,用this.touch來記錄位置信息
onShortcutTouchMove(e){
this.touch.y2=e.touches[0].pageY;//開始的y座標
let delta=(this.touch.y2-this.touch.y1)/CUTHEIGHT | 0//向下取整。獲取到最接近的值
let anchorIndex=parseInt(this.touch.anchorIndex)+delta
this._scollTo(anchorIndex);
},
複製代碼
當咱們滑動左側的時候,咱們須要監聽咱們當前滑動的縱向y的值,來判斷當前滑動的是哪個區間,來肯定應該指向的索引