使用
1 使用我的 scroll 組件,並且安裝依賴
2 複製代碼
3 需要給 indexlist 組件一個固定的高度
4 可以根據 class 名,自定義樣式,修改dom 結構的話就要修改源碼了
預覽
<template>
<div>
<scroll class="scroll"
:data='singerList'
:pullUpLoad='upload'
@pullUpLoad='pullUpLoad'
:pullDownRefresh='refresh'
@pullDownRefresh='downRefresh'
@getScroll='getScroll'
>
<div class="singList" ref="singList">
<dl class="singItem"
v-for="(item,idx) in singerList"
:key=item.title
:id='"dom"+idx'>
<dt :class='idx == index&&idx!=0?"hidden":""'>{{item.title}}</dt>
<dd v-for="ite in item.list" :key='ite.Fsinger_id'>
<img v-lazy="'//y.gtimg.cn/music/photo_new/T001R150x150M000'+ite.Fsinger_mid+'.jpg?max_age=2592000'">
<span>{{ite.Fsinger_name}}</span>
</dd>
</dl>
</div>
</scroll>
<!-- 右側bar -->
<div class="navBar" ref="navBar" @touchstart="touchstartNavBar" @touchmove='touchmoveNavBar'>
<span v-for="(item,idx) in navBar" :class="idx==index?'active':''"
:key='idx' ref='navBarItem'
:indexId = 'idx'
>
{{item}}
</span>
</div>
<!-- 頂部fixbar -->
<div class="fixBar" v-show="showFixBar">{{navBar[index]}}</div>
</div>
</template>
<script>
import { getSingerList } from "@/api/singer.js";
import scroll from "@/util/components/BScroller.vue";
export default {
data() {
return {
singerList:[],
scroll:null,
navBar:[],
index:0,
showFixBar:false
}
},
// 在這裏定義一個變量和在data 裏面的區別是,這裏面的不需要動態渲染到頁面上
created(){
this.toTopHeight = 0;
this.firstPageY = 0;
this.itemHeight = 0;
this.navBarSize = 0;
},
mounted() {
getSingerList().then(res => {
let list = res.data.list;
let singerList = {};
list.forEach(e => {
if(singerList[e.Findex] == undefined){
singerList[e.Findex] = [];
}
singerList[e.Findex].push(e);
});
let array = [];
for(let i in singerList){
array.push({
title:i,
list:singerList[i]
})
}
// 排序
array = array.sort((a,b)=>{
// return a.title.charCodeAt(0) - b.title.charCodeAt(0)
if(a.title>b.title){
return 1;
}else if(a.title<b.title){
return -1;
}else{
return 0;
}
})
this.singerList = array;
// console.log(this.singerList);
// 獲取navBar
this.getNavBar();
});
},
methods:{
downRefresh(finash){
this.$emit('Refresh',finash);
},
pullUpLoad(finash){
this.$emit('UpLoad',finash);
},
getScroll(scroll){
this.scroll = scroll;
// 動態計算滾動區域距離頂部的高度
this.toTopHeight = this.$refs.singList.getBoundingClientRect().top;
this.scroll.on("scroll",(pos)=>{
// console.log(this.index)
console.log(pos.y)
// 判斷滾動的方向
//根據index 獲取當前顯示在屏幕中的dom
let nowDom = document.getElementById("dom"+this.index);
// 如果向下滑動的時候,最上面的dom 是 nowdom的時候,也就是id 是0
// 的dom是nowdom的時候,繼續往下滑,border-top距離頂部的距離會由負數變成正數,這個時候
// index 會別 --,變爲-1,然後再次執行getElementById 的時候會是null
// 這個時候可以通過一些判斷來結局
// if(nowDom===null&&this.index<0){
// this.index =0;
// nowDom = document.getElementById("dom"+this.index);
// }
// 一旦滾動到最頂端的時候,還往下拉這個時候就要隱藏fixBar
// 到了最頂端還往下拉那麼說明y是>0 的,並且還會增大
/**
* 思考過程
* 1 向下滾動的過程中,內部dom的頂部距離外層上邊框的距離是在增加的,
* 2 滾動到最頂部的時候, 距離是0,再往下拉是下拉刷新,距離是整數,
* 3 往上滑動距離是負數,
* 4 往上滑動的過程中,pos 的值是減小的,所以一旦 <n (小於某個值)的時候可以做一個判斷
*
* **/
if(pos.y > -10){
if(pos.y>10)return;
this.showFixBar = false;
}
else{
if(pos<-30)return;
this.showFixBar = true;
}
// console.log(nowDom);
if(this.scroll.movingDirectionY===1){
// console.log("上化")
// 向上滾動
// 如果dom 底部 距離窗口頂部的距離<0, 那麼說明這個dom
// 被徹底的滾動到屏幕外面了
let distanceToTop = nowDom.getBoundingClientRect().bottom - this.toTopHeight;
// console.log(distanceToTop)
if(distanceToTop<0){
// 已經滾動到屏幕外面了,下一個進入nowdom,
// 這個時候改變index ,這樣就可以隨着向下滾動,獲取的nowdom 始終是在屏幕中dom
this.index ++;
}
}else{
// console.log("下")
// 向下滾動
let distanceToTop = nowDom.getBoundingClientRect().top - this.toTopHeight;
// 這個時候這個dom 被下滾動,並且上邊框已經從窗口頂部出來了,他的上一個元素
// 變爲nowdom
// console.log(distanceToTop)
// 防止出現負數
if(distanceToTop>0&&this.index>0){
this.index --;
}
}
})
},
getNavBar(){
this.navBar = this.singerList&&this.singerList.map((item)=>{
return item.title;
})
let allNum = this.singerList.length;
this.navBarSize = allNum;
// 獲取一個navbarItem 的高度
let height = this.$refs.navBar.offsetHeight;
this.itemHeight = height/allNum;
},
touchstartNavBar(e){
//獲取 pageY的值,給touchmove 用
this.firstPageY = e.touches[0].pageY;
let id = e.target.getAttribute("indexId");
// 有時候會點擊到dom的空白處,沒有點擊到這個dom 也就獲取不到id
if(id===null)return;
// 修改index 標誌,告訴其他的dom 目前是這個item是nowdom,
// 相關的地方會自動的修改
this.index = Number(id);
setTimeout(()=>{
this.scrollTo();
console.log(this.index);
},30)
console.log(id)
console.log("觸發move 事件")
},
touchmoveNavBar(e){
// e.currentTarget 綁定事件的dom
// e.target 被點擊的最內層的dom
// e.target 也可以是移動之前觸碰的第一個dom
// 獲取當前手指的pageY
// 計算出偏移量
// 獲取絕對值
let offetY = e.touches[0].pageY - this.firstPageY;
// 獲取第一個位置的dom 和 index 值
let firstIndex =e.target.getAttribute('indexId');
if(firstIndex === null)return;//沒有點擊在字母上,點擊在了其他地方
//獲取一個字母的高度
// 偏移量除以一個item的高度,計算出偏移了幾個item
let num = Math.floor(offetY/this.itemHeight);
// console.log("偏移的個數"+num);
// console.log("firstIndex===="+firstIndex);
// console.log(this.index);
console.log(e.target.getAttribute('indexId'))
// console.log(this.itemHeight)
let index = Number(firstIndex)+num;
// 如果滑到最頂部和最後一個的時候不
if(index+1>this.navBarSize)return;
if(index<0)return;
this.index = index;
this.scrollTo();
},
scrollTo(){
// 根據index 獲取dom
let dom = document.getElementById(`dom${this.index}`);
// 滾動到dom 所在的位置
this.scroll.scrollToElement(dom);
}
},
computed:{
},
components:{
scroll,
},
props:{
singerList:{
type:Array,
default(){
return []
}
},
refresh:{
default:false
},
upload:{
default:false
}
},
};
</script>
<style scoped lang='scss'>
.scroll{
height:88vh;
border: 1px solid darkgoldenrod;
.singList{
border: 1px solid springgreen;
.singItem{
border-bottom: 1px solid darkorange;
dt{
height: 8vmin;
// border: 1px solid darkcyan;
line-height: 8vmin;
padding-left: 12vmin;
}
dd{
margin:5vmin;
display: flex;
align-items: center;
img{
height: 18vmin;
width: 18vmin;
border-radius: 50%;
}
span{
margin-left:5vmin;
}
}
}
}
}
.navBar{
position: absolute;
top:20vh;
right: 0px;
display: flex;
flex-direction: column;
background-color: rgba(0,0,0,0.3);
padding:0vmin 1.2vmin;
border-radius:50px;
height:60vh;
justify-content: space-around;
.active{
color:rgb(112, 213, 49);
}
}
.fixBar{
height: 8vmin;
width: 100vw;
line-height: 8vmin;
padding-left: 12vmin;
background-color:rgba(255,255,255,0.1);
position: fixed;
top:12vh;
}
.hidden{
opacity: 0;
}
</style>
使用案例
<template>
<div>
<indexList class="indexList"
:singerList = 'singerList'
@Refresh='downRefresh'
@UpLoad='pullUpLoad'
:upload =true
:refresh = {stop:50}
/>
</div>
</template>
<script>
import indexList from '@/util/components/indexList.vue';
import {getSingerList} from '@/api/singer.js';
export default {
data(){
return{
singerList:[]
}
},
components:{
indexList,
},
mounted(){
this.getList();
},
methods:{
getList(){
getSingerList().then(res => {
let list = res.data.list;
let singerList = {};
list.forEach(e => {
if(singerList[e.Findex] == undefined){
singerList[e.Findex] = [];
}
singerList[e.Findex].push(e);
});
let array = [];
for(let i in singerList){
array.push({
title:i,
list:singerList[i]
})
}
// 排序
array = array.sort((a,b)=>{
// return a.title.charCodeAt(0) - b.title.charCodeAt(0)
if(a.title>b.title){
return 1;
}else if(a.title<b.title){
return -1;
}else{
return 0;
}
})
this.singerList = array;
// console.log(this.singerList);
// 獲取navBar
// this.getNavBar();
});
},
downRefresh(finash){
console.log("刷新");
setTimeout(()=>{
finash();
},2000)
},
pullUpLoad(finash){
console.log("加載更多");
setTimeout(()=>{
finash(true);
},2000)
}
}
}
</script>
<style lang="scss">
.indexList{
height: 88vh;
}
//最外層
.singList{
// 包括title 在內的一個item
.singItem{
// 標題
dt{
}
// 一個字母對應的循環列表
dd{
// 右邊圖片
img{
}
// 左邊文字
span{
}
}
}
}
</style>
注意:
1 使用fastclick 可以防止右側導航欄的穿透。