通过使用scroll-view
组件来实现顶部的菜单列表、多个列表的左右滑动实现父组件及滚动列表,同时监听触摸相关事件来处理左右滑动整个列表而不是滑一部分移动一部分、对列表上下滚动和左右滑动的优化及配合translateY
实现scroll-view
组件的上拉加载和下拉刷新。
基本的wxml文件实现布局:
<view class="page-con">
<scroll-view class="nav-list" scroll-x="{{true}}" scroll-into-view="{{navList[activeNavIndex].name}}" scroll-with-animation="true" scroll-anchoring="true">
<view id="{{item.name}}" class="nav-item {{activeNavIndex === index ? 'active' : ''}}" wx:for="{{navList}}" wx:key="index" bindtap="switchPage" data-navIndex="{{index}}">{{item.name}}view>
scroll-view>
<scroll-view class="list-con" scroll-x="{{canSwitch}}" scroll-into-view="{{navList[activeNavIndex].name}}" scroll-with-animation="true" scroll-anchoring="true">
<scroll-view id="{{item.name}}" class="list-item" wx:for="{{navList}}" wx:key="index" scroll-y="true" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" bindtouchcancel="touchCancel" bindscroll="listScroll" bindscrolltolower="bottomLoad" style="transform: translateY({{freshDistance}}px)">
<view class="top-fresh" hidden="{{!item.isFresh}}">view>
<view class="card-item" wx:for="{{item.cardNum}}" wx:for-item="cardItem" wx:for-index="cardIndex" wx:key="cardIndex">{{item.name}}-{{cardIndex}}view>
<view class="top-fresh" hidden="{{!item.showLoading}}">view>
scroll-view>
scroll-view>
view>
wxss文件实现页面UI的优化:
.page-con{
width: 100vw;
height: 100vh;
background: #f5f5f5;
}
.nav-list{
position: sticky;
top: 0;
width: 100%;
padding: 10rpx 20rpx;
box-sizing: border-box;
background: #fff;
white-space: nowrap;
}
.nav-item{
display: inline-block;
height: 40rpx;
line-height: 40rpx;
padding: 10rpx 20rpx;
background: #f5f5f5;
border-radius: 6rpx;
margin-right: 12rpx;
}
.nav-item.active{
background: #2CAE73;
color: #fff;
}
.list-con{
width: 100%;
height: calc(100% - 80rpx);
white-space: nowrap;
}
.list-item{
display: inline-block;
width: 100vw;
height: 100%;
box-sizing: border-box;
transition: all 200ms;
}
.card-item{
width: calc(100% - 48rpx);
height: 300rpx;
background: #fff;
border-radius: 8rpx;
margin: 24rpx auto 0;
text-align: center;
line-height: 300rpx;
}
.top-fresh{
padding: 10rpx 0;
display: flex;
justify-content: center;
align-items: center;
}
.top-fresh::after{
content: '';
width: 30rpx;
height: 30rpx;
border-radius: 15rpx;
border: 4rpx solid #2CAE73;
border-left-color: transparent;
border-bottom-color: transparent;
border-right-color: transparent;
animation: freshLoading 600ms linear infinite;
}
@keyframes freshLoading {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
js实现相关功能:
let pageData = {};
Page({
data: {
navList:[
{ id: 0, name: 'test1', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 1, name: 'test2', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 2, name: 'test3', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 3, name: 'test4', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 4, name: 'test5', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 5, name: 'test6', isFresh: false, showLoading: false, cardNum: 20 },
{ id: 6, name: 'test7', isFresh: false, showLoading: false, cardNum: 20 }
],
activeNavIndex: 0,
canSwitch: true,
freshDistance: 0
},
onPullDownRefresh() {
wx.stopPullDownRefresh();
},
switchPage(e) {
//实现点击菜单切换列表
const { navindex = 0 } = e.currentTarget.dataset;
this.setData({ activeNavIndex: navindex });
},
touchStart(e) {
//记录开始触摸时的坐标,后续用来进行切换滚动的优化及下拉刷新的距离计算。
const { clientX = 0, clientY = 0 } = e.changedTouches[0];
Object.assign(pageData, { startX: clientX, startY: clientY });
},
touchMove(e) {
//手指移动时判断是左右滑动还是上下滚动。手指向下拉时记录下拉的距离;当上下滑动的距离大于左右距离且小于5时也记录下拉距离同时使左右滑动失效。
const { clientX = 0, clientY = 0 } = e.changedTouches[0], { startY = 0, startX = 0 } = pageData;
let { canSwitch = true, navList = [], activeNavIndex = 0 } = this.data;
let distanceY = clientY - startY, distanceX = clientX - startX;
if (canSwitch){
if (Math.abs(distanceX) < Math.abs(distanceY) && Math.abs(distanceX) < 5){
if (distanceY > 0) {
Object.assign(navList[activeNavIndex], { isFresh: true, });
this.setData({ freshDistance: distanceY, canSwitch: false, navList });
}
}
}else{
if (distanceY > 0) {
Object.assign(navList[activeNavIndex], { isFresh: true, });
this.setData({ freshDistance: distanceY, canSwitch: false, navList });
}
}
},
touchEnd(e) {
//触摸结束时判断若此时左右滑动无失效时判断左右滑动方向来切花列表;失效时表明是上下滚动,同时若是下拉判断下拉距离达到刷新的距离时触发刷法模拟刷新。
const { clientX = 0 } = e.changedTouches[0], { startX = 0 } = pageData;
let { activeNavIndex = 0, canSwitch = true, navList = [], freshDistance = 0 } = this.data;
if (canSwitch){
let distanceX = clientX - startX, direction = distanceX > 0 ? 'switchLeft' : (distanceX < 0 ? 'switchRight' : 'current');
distanceX = Math.abs(distanceX);
if (distanceX >= 20) {
if (direction === 'current') {
this.setData({ activeNavIndex });
} else if (direction === 'switchLeft') {
this.setData({ activeNavIndex: activeNavIndex ? activeNavIndex - 1 : activeNavIndex });
} else if (direction === 'switchRight') {
this.setData({ activeNavIndex: activeNavIndex === navList.length - 1 ? activeNavIndex : activeNavIndex + 1 });
}
} else {
this.setData({ activeNavIndex });
}
}else{
if (freshDistance < 20) {
this.setData({ freshDistance: 0, canSwitch: true });
} else {
this.setData({ freshDistance: 20 }, () => {
setTimeout(() => {
Object.assign(navList[activeNavIndex], { isFresh: false, cardNum: 20 });
this.setData({ freshDistance: 0, canSwitch: true, navList })
}, 2000);
});
}
}
},
touchCancel(e) {
//触摸结束时判断若此时左右滑动无失效时判断左右滑动方向来切花列表;失效时表明是上下滚动,同时若是下拉判断下拉距离达到刷新的距离时触发刷法模拟刷新。
const { clientX = 0 } = e.changedTouches[0], { startX = 0 } = pageData;
let { activeNavIndex = 0, canSwitch = true, navList = [], freshDistance = 0 } = this.data;
if (canSwitch) {
let distanceX = clientX - startX, direction = distanceX > 0 ? 'switchLeft' : (distanceX < 0 ? 'switchRight' : 'current');
distanceX = Math.abs(distanceX);
if (distanceX >= 20) {
if (direction === 'current') {
this.setData({ activeNavIndex });
} else if (direction === 'switchLeft') {
this.setData({ activeNavIndex: activeNavIndex ? activeNavIndex - 1 : activeNavIndex });
} else if (direction === 'switchRight') {
this.setData({ activeNavIndex: activeNavIndex === navList.length - 1 ? activeNavIndex : activeNavIndex + 1 });
}
} else {
this.setData({ activeNavIndex });
}
} else {
if (freshDistance < 20) {
this.setData({ freshDistance: 0, canSwitch: true });
} else {
this.setData({ freshDistance: 20 }, () => {
setTimeout(() => {
Object.assign(navList[activeNavIndex], { isFresh: false, cardNum: 20 });
this.setData({ freshDistance: 0, canSwitch: true, navList })
}, 2000);
});
}
}
},
//自定义下拉刷新
listScroll(e) {
//列表滚动时使左右切换失效
this.setData({ canSwitch: false });
},
bottomLoad(e){
//上拉加载更多数据
let { activeNavIndex = 0, navList = [] } = this.data;
Object.assign(navList[activeNavIndex], { showLoading: true, });
this.setData({ navList }, () => {
setTimeout(() => {
Object.assign(navList[activeNavIndex], { showLoading: false, cardNum: navList[activeNavIndex].cardNum + 20 });
this.setData({ navList });
}, 2000);
});
}
})