今天在做百度小程序的转换,发现真机上用之前的swiper-item结合scroll-view 的效果不理想,于是我重新思考,发现了一种更合适的方案。
之前的缺陷
- swiper-item里面的内容使用的是view组件,导致每次切换到新的swiper-item时,历史定位都重置了。这样导致了我每次切换到另外一个swiper-item时要计算他的滚动位置和他的全部元素高度。
- 我还需要频繁记录每次滚动的定位,保存起来,以便下次用的时候来拿,使用scroll事件很卡。
- 上面返回历史位置时,最外层的scroll-view组件都要重新赋值scrollTop值,导致内容每次都要从头滚动,很消耗性能,并且还不是实时的,比如百度小程序(响应不及时)里就放大了这个bug了,当我切换到下一屏时,内容已经生成了,但是位置没有定位,要等零点几秒才能定位到那个位置,就是说你能看到内容在从头滚动。
解决方法
https://github.com/zhongjie-chen/wx-scrollable-tab-view 在网上找到这个人使用touch事件,我试着用他的方法,很好用,但是有一点缺陷,就是手指切换有时候不灵敏,有时候我已经滑动很长了,但它没有切换到下一屏,还在当前屏停留。然后我认真的看了他的实现,发现他里面解决了这个scroll-view定位由于滑动到下一屏会重置的问题。
他是怎么解决的呢?就是每一个切换里面又加多了一个scroll-view。这样子就避免了切换时历史滚动位置需要重置的问题。于是我想到了既然用他这种做法解决了我前面的缺陷,那我把两个结合起来,不就完美解决了。
首先是使用swiper组件解决切换问题,然后就是每个swiper-item里面加一个scroll-view组件,这样子就不需要每次切换都要计算历史滚动位置了。效果非常流畅,JS只需要几个常规的函数就解决了,不需要hack,完美的解决方案。
代码如下
wxml:
<!--pages/live/live.wxml-->
<view class="g-doc">
<view class="tab">
<view class="tab-inner">
<scroll-view class="scroll-bangdan" scroll-x="true" scroll-left="{=scrollLeft=}">
<view class="ctrl" style="width:1054rpx">
<block s-for="item, index in nav" s-key="{{item.columnId}}">
<view class="toc{{current==index?' cur':''}}" bindtap="changeTab" data-index="{{index}}">
<view class="text">
<text>{{item.columnName}}</text>
</view>
</view>
</block>
</view>
</scroll-view>
<view class="mask"></view>
</view>
</view>
<view class="tab-container">
<swiper current="{{current}}" class="swiper-box" duration="300" bindchange="bindchange">
<block s-for="item, index in nav" s-key="{{item.columnId}}">
<swiper-item>
<block s-if="list[item.columnId].data.length>0">
<scroll-view style="height:100%" scroll-y="true" bindscrolltolower="loadMoreList">
<view class="list">
<block s-for="art, index in list[item.columnId].data" s-key="{{art.id}}">
<view class="item">
<navigator url="../art/art?id={{art.id}}" hover-class="none" open-type="navigate">
<view class="img">
<image mode src="{{art.imageUrl}}"></image>
<view class="meta">
<view class="avatar">
<image src="{{art.userImage}}"></image>
</view>
<view class="nickName">
<text>{{art.userName}}</text>
</view>
</view>
</view>
<view class="title">{{art.title}}</view>
<view class="des">{{art.summary}}</view>
</navigator>
</view>
</block>
<view class="m-end" hidden="{{list[curListId].endTipHidden}}">
<block s-if="endTip=='正在加载'">
<view class="icon"></view>
</block>
{{endTip}}
</view>
<view class="m-end" hidden="{{!list[curListId].endTipHidden}}">没有更多了</view>
</view>
</scroll-view>
</block>
</swiper-item>
</block>
</swiper>
</view>
</view>
js:
// pages/live/live.js
const config = require('../../pc.config.js');
Page({
/**
* 页面的初始数据
*/
data: {
nav: [{
"columnName": '全部',
"columnId": "000081525"
}, {
"columnName": '好物种草',
"columnId": "000084624"
}, {
"columnName": '收纳支招',
"columnId": "000084644"
}, {
"columnName": '细节控',
"columnId": "000084645"
}, {
"columnName": '小改造',
"columnId": "000084625"
}, {
"columnName": '洗刷刷',
"columnId": "000084626"
}, {
"columnName": '睡眠研究',
"columnId": "000084646"
}],
list: {},
curListId: 0,
endTipHidden: false,
endTip: '正在加载',
current: 0,
scrollLeft: 0,
loading: false
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var index = options.index || 0;
this.getNav(index);
},
timer: null,
//获取导航和节点
getNav: function (index) {
var self = this;
swan.request({
url: config.getAPI('liveNav'),
success: function (res) {
// console.log(res);
self.setData({
nav: res.data.livingColumn,
current: index
});
//获取导航的初始位置
if (index > res.data.livingColumn.length / 2) {
self.setData({
scrollLeft: 1054 / 2
});
}
self.getList(res.data.livingColumn[index].columnId);
}
});
},
//滑到底部加载更多
loadMoreList: function () {
var self = this;
clearTimeout(self.timer);
self.timer = null;
self.timer = setTimeout(function () {
var list = self.data.list;
var cid = self.data.curListId;
if (list[cid].pageNo < list[cid].pageCount) {
self.getList(cid);
}
}, 300);
},
//请求列表
getList: function (cid) {
var self = this;
this.setData({
curListId: cid
});
var list = this.data.list;
if (this.data.loading) return;
this.setData({
loading: true
})
if (!list[cid] || (list[cid] && (list[cid].pageNo < list[cid].pageCount))) {
var pageNo = !list[cid] ? 1 : list[cid].pageNo + 1;
swan.request({
url: config.getAPI('liveList') + `?columnType=livingColumn&columnId=${cid}&pageSize=10&pageNo=${pageNo}`,
success: function (res) {
// console.log(res.data);
var obj = {};
obj.pageNo = res.data.pageNo;
obj.pageCount = Math.ceil(res.data.total / res.data.pageSize);
obj.total = res.data.total;
obj.data = !list[cid] ? res.data.data : list[cid].data.concat(res.data.data);
obj.swiperHeight = 1110;
obj.swiperHeight = res.data.total > res.data.pageSize * res.data.pageNo ? res.data.pageSize * res.data.pageNo * 518 + 102 : res.data.total * 518 + 102;
if (res.data.pageNo * res.data.pageSize >= res.data.total) {
obj.endTip = '没有更多了';
obj.endTipHidden = true;
} else {
obj.endTip = '正在加载';
}
list[cid] = obj;
self.setData({
list,
loading: false
});
},
fail: function () {
swan.showModal({
title: '提示',
content: '加载不成功,是否重新加载?',
confirmText: "重新加载",
success: function (res) {
self.setData({
loading: false
})
if (res.confirm) {
self.loadMoreList();
} else if (res.cancel) {
// console.log('用户点击取消')
}
}
});
}
});
}
},
switchNav: function (index) {
if (index && this.data.current == index) return;
var cid = this.data.nav[index].columnId;
var self = this;
var scrollLeft = 0;
if (index > this.data.nav.length / 2) {
scrollLeft = 1054 / 2;
}
var list = self.data.list;
if (!list[cid]) {
clearTimeout(self.timer);
self.timer = null;
self.timer = setTimeout(function () {
self.setData({
curListId: cid,
current: index,
scrollLeft
});
self.getList(cid);
}, 500);
} else {
self.setData({
curListId: cid,
current: index,
scrollLeft
});
}
},
//切换导航
changeTab: function (e) {
// console.log('changetab');
var index = e.currentTarget.dataset.index;
this.switchNav(index);
},
//滑动swiper
bindchange: function (e) {
// console.log('changeswiper');
var index = e.detail.current;
if (e.detail.source && e.detail.source == 'touch') {
this.switchNav(index);
}
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
return {
path: '/pages/live/live?index=' + this.data.current
};
}
});
/* pages/live/live.css */
page {
height: 100%;
overflow: hidden;
}
.g-doc {
height: 100%;
}
.tab {
position: fixed;
top: 0;
left: 0;
border-bottom: 1px solid #f8f8f8;
background: #fff;
z-index: 1;
width: 100%;
height: 100rpx;
line-height: 100rpx;
}
.tab-inner{
height: 100rpx;
overflow: hidden;
}
/* .tab-con{width: 100%;} */
.scroll-bangdan {
width: 100%;
position: relative;
height: 130rpx;
}
.tab .ctrl {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-flow: row wrap;
-webkit-flex: row wrap;
font-size: 28rpx;
height: 100rpx;
line-height: 100rpx;
}
.tab .ctrl .toc {
text-align: center;
margin-left: 50rpx;
white-space: nowrap;
}
.tab .ctrl .toc:first-child {
margin-left: 40rpx;
}
.tab .ctrl .toc:last-child {
margin-right: 40rpx;
}
.tab .ctrl .toc .text {
display: inline-block;
position: relative;
}
.tab .ctrl .toc.cur .text::before {
content: "\20";
display: block;
position: absolute;
height: 10rpx;
background: #fbe251;
left: 0;
bottom: 30rpx;
width: 100%;
z-index: 0;
}
.tab .ctrl .toc .text text {
position: relative;
z-index: 2;
}
.tab .mask {
background: url(https://www1.pchouse.com.cn/2018/weixinminipro/mask.png) no-repeat top right;
background-size: 113rpx;
width: 83rpx;
height: 100rpx;
position: fixed;
right: 0;
top: 0;
z-index: 50;
}
.swiper-box{position: absolute; left: 0; width: 100%; height: 100%; top: 0;}
.list {
padding-top: 100rpx;
}
.list .item {
padding: 30rpx 40rpx 40rpx;
}
.list .item .img {
width: 670rpx;
height: 336rpx;
overflow: hidden;
position: relative;
border-radius: 16rpx;
}
.list .item .meta {
position: absolute;
left: 0;
bottom: 0;
height: 84rpx;
width: 100%;
padding-top: 16rpx;
line-height: 60rpx;
color: #fff;
font-size: 28rpx;
overflow: hidden;
background: url(https://www1.pchouse.com.cn/2018/weixinminipro/pic-modal.png?v2) repeat-x center bottom;
background-size: auto 99rpx;
}
.list .item .meta .avatar {
width: 60rpx;
height: 60rpx;
float: left;
margin-left: 24rpx;
margin-right: 24rpx;
}
.list .item .meta .avatar image {
width: 60rpx;
height: 60rpx;
border-radius: 100%;
}
.list .item .meta .nickName {
float: left;
}
.list .item .img image {
width: 670rpx;
height: 336rpx;
border-radius: 16rpx;
}
.list .item .title {
height: 73rpx;
line-height: 73rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 36rpx;
}
.list .item .des {
height: 40rpx;
overflow: hidden;
font-size: 24rpx;
color: #aaa;
text-overflow: ellipsis;
white-space: nowrap;
}
.m-end {
padding: 20rpx 0 34rpx;
line-height: 28rpx;
}