小程序之tab如何实现swiper自适应高度,并记录滚动位置?

2018-10-31 18:25:00 5192 4 编辑:深正互联 来源:互联网

移动端中需要使用swiper插件实现tab切换和手势滑动的(类似于今日头条资讯列表),在各种APP上我们可以很常见,但在小程序上实现这个看起来有点难,swiper插件滑动到下一屏的时候位置总是会回到跟上一屏相同的位置。我记得微信刚出来不久,有一个这样子的需求,那时候是另外一个同事在跟,跟我说到过这个功能无法实现。最近我也接到这个需求,于是我认真的看了一下微信组件文档,发现swiper+scroll-view组件结合是可以实现这个功能。


需要说的是,我的每个swiper-item数据不是固定的,每个swiper-item列表数据都有滚动底部会无线加载,所以说我无法在一开始就确定了所有的子项的高度,另外每个swiper-item都需要滚动,这就是说它们的滚动到的位置每个都不是一样的。每次滑动到下一屏的时候,要确定最外层的scroll-view的scroll-top值。


自适应高度

由于swiper组件并不是自适应高度的,而我们每个swiper-item的高度并不是一样的,所以第一步就是计算每个swiper-item的高度,并赋值给swiper组件。WXML代码如下:

<swiper current="{{current}}" style="height:{{list[curListId].swiperHeight}}rpx;" class="swiper-box" duration="300" bindchange="bindchange">

    <block wx:for="{{nav}}">

        <swiper-item>

            <block wx:if="{{list[item.columnId].data.length>0}}">

                <view class="list">

                    <block wx:for="{{list[item.columnId].data}}" wx:for-item="art">

                        <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>

            </block>

        </swiper-item>

    </block>

</swiper>


可以看到我里面定义了一个循环,把所有子项的数据都放在list里面。当然前提我们已经知道要判断的list[item.columnId]是否已经存在值,如果没有我们需要先请求数据。而每个对象对应的下标我们已经知道,所以我们一开始进来就确认了所有swiper-item要加载的内容,后续滑动加载内容时如果没有,我们就会去请求,如果有则使用,并且滑动到底部时,我们根据curListId知道当前需要请求那个对象。把剩下的数据合并在同一个对象里面。


计算swiper-item的高度,可以使用微信提供的一个 api createSelectorQuery,我这里没用,因为我的列表每一个字内容都是固定好高度的。所以我只要知道获取的数据数组的长度是多少,然后计算每个子项的高度,就能得到swiper-item的高度了。


记录滚动值

记录滚动值,这个很简单,因为我们的页面最外层就是一个scroll-view组件,所以我们只要给这个scroll-view一个bindscroll事件,在滚动的过程中,不断的记录更新每一个子项它最后滚动到的位置,下次进入这一屏,就看看数据里面有没有这个滚动值,没有的话,就是第一次进入,默认为0,如果有值,说明之前我们已经滚动过一次,则赋值给scroll-view的scroll-top。

scroll: function(e) {

    var self = this;

    setTimeout(function() {

        console.log(e.detail.scrollTop);

        var list = self.data.list;

        if (list[self.data.curListId]) {

            list[self.data.curListId].scrollTop = e.detail.scrollTop;

            self.setData({

                list

            })

        }

    }, 300);

},


预览和代码下载

就是实现这种常见的资讯tab列表,需要滑动加载数据,切换自动回到上次滚动的位置。


小程序

JS:

const app = getApp()

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"

        }],

        navPosition: [],

        list: {},

        curListId: 0,

        endTipHidden: false,

        endTip: '正在加载',

        current: 0,

        scrollLeft: 0,

        px2rpx: 2,

        winWidth: 375,

        scrollTop: 0

    },


    /**

     * 生命周期函数--监听页面加载

     */

    onLoad: function(options) {

        var index = options.index || 0;

        this.getSystem();

        this.getNav(index);

    },

    timer: null,

    getSystem: function() {

        var self = this;

        wx.getSystemInfo({

            success: function(res) {

                console.log(res);

                self.setData({

                    winWidth: res.windowWidth,

                    px2rpx: 750 / res.windowWidth

                })

            },

        });


    },

    //获取导航和节点

    getNav: function(index) {

        var self = this;

        wx.request({

            url: config.getAPI('liveNav'),

            success: function(res) {

                //   console.log(res);

                self.setData({

                    nav: res.data.livingColumn,

                    current: index

                });

                //获取导航的初始位置

                const query = wx.createSelectorQuery()

                query.selectAll('.toc').boundingClientRect();

                //   query.selectViewport().scrollOffset()

                query.exec(function(res) {

                    console.log(res);

                    console.log(res[index]);

                    self.setData({

                        navPosition: res[0]

                    })

                    if (index >= 4) {

                        self.setData({

                            scrollLeft: res[0][index].left

                        })

                    }

                })


                self.getList(res.data.livingColumn[index].columnId);

            }

        })

    },

    //滑到底部加载更多

    loadMoreList: function() {

        var list = this.data.list;

        var cid = this.data.curListId;

        if (list[cid].pageNo < list[cid].pageCount) {

            this.getList(cid);

        }

    },

    //请求列表

    getList: function(cid) {

        var self = this;

        this.setData({

            curListId: cid

        })

        var list = this.data.list;

        if (!list[cid]) {

            wx.request({

                url: config.getAPI('liveList') + `?columnType=livingColumn&columnId=${cid}&pageSize=10&pageNo=1`,

                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 = res.data.data;

                    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

                    })

                }

            })

        } else {

            if (list[cid].pageNo < list[cid].pageCount) {

                wx.request({

                    url: config.getAPI('liveList') + `?columnType=livingColumn&columnId=${cid}&pageSize=10&pageNo=${list[cid].pageNo+1}`,

                    success: function(res) {

                        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].data.concat(res.data.data);

                        if (res.data.pageNo * res.data.pageSize >= res.data.total) {

                            obj.endTip = '没有更多了';

                            obj.endTipHidden = true;

                        } else {

                            obj.endTip = '正在加载';

                        }

                        obj.swiperHeight = res.data.total > res.data.pageSize * res.data.pageNo ? res.data.pageSize * res.data.pageNo * 518 + 102 : res.data.total * 518 + 102;

                        list[cid] = obj;

                        self.setData({

                            list

                        })

                    }

                })

            }

        }

    },

    //切换导航(包含滑动swiper和切换导航跳转)

    switchNav: function(index) {

        if (index && this.data.current == index) return;

        console.log('switchtab');

        var cid = this.data.nav[index].columnId;

        var self = this;

        var scrollLeft = 0;

        if (self.data.navPosition[index].right * self.data.px2rpx + 62 >= 750) {

            scrollLeft = self.data.navPosition[index].left;

        }

        var list = self.data.list;

        var scrollTop = 0;

        if (list[cid] && list[cid].scrollTop) {

            scrollTop = list[cid].scrollTop

        }


        if (!list[cid]) {

            clearTimeout(self.timer);

            self.timer = null;

            self.timer = setTimeout(function() {


                self.setData({

                    curListId: cid,

                    current: index,

                    scrollLeft,

                    scrollTop

                })

                self.getList(cid);

            }, 500);

        } else {

            self.setData({

                curListId: cid,

                current: index,

                scrollLeft,

                scrollTop

            })

        }


    },

    //切换导航

    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;

        //加上这个避免swiper过程,swiper-item会发生滑动混乱,滑动过快就会一直在闪动,新的API属性,touch说明是用户接触滑动,而不是自动滑动

        if (e.detail.source && e.detail.source =='touch'){

            this.switchNav(index);

        }

        

    },

    //滚动记录之前的滚动位置

    scroll: function(e) {

        var self = this;

        setTimeout(function() {

            console.log(e.detail.scrollTop);

            var list = self.data.list;

            if (list[self.data.curListId]) {

                list[self.data.curListId].scrollTop = e.detail.scrollTop;

                self.setData({

                    list

                })

            }

        }, 300);

    },

})


WXML:

<scroll-view class="g-doc" scroll-y="true" bindscrolltolower="loadMoreList" scroll-top="{{scrollTop}}" bindscroll="scroll">

    <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 wx:for="{{nav}}">

                        <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}}" style="height:{{list[curListId].swiperHeight}}rpx;" class="swiper-box" duration="300" bindchange="bindchange">

            <block wx:for="{{nav}}">

                <swiper-item>

                    <block wx:if="{{list[item.columnId].data.length>0}}">

                        <view class="list">

                            <!-- <view class="nodata">这里空空如也</view> -->

                            <block wx:for="{{list[item.columnId].data}}" wx:for-item="art">

                                <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>

                    </block>

                </swiper-item>

            </block>

        </swiper>


        <view class="m-end" hidden="{{list[curListId].endTipHidden}}">

            <block wx:if="{{endTip=='正在加载'}}">

                <view class="icon"></view>

            </block>

            {{endTip}}

        </view>

        <view class="m-end" hidden="{{!list[curListId].endTipHidden}}">没有更多了</view>

    </view>

</scroll-view>


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;

}


.list {

    padding-top: 100rpx;

}


.list .item {

    padding: 30rpx 40rpx 40rpx;

}


.list .item .img {

    width: 670rpx;

    height: 336rpx;

    overflow: hidden;

    position: relative;

}


.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;

}

本站文章均为深正网站建设摘自权威资料,书籍,或网络原创文章,如有版权纠纷或者违规问题,请即刻联系我们删除,我们欢迎您分享,引用和转载,但谢绝直接搬砖和抄袭!感谢...
关注深正互联

15

技术从业经验

多一份方案,会有收获...

联系深正互联,免费获得专属《策划方案》及报价

在线咨询
微信交谈
拒绝骚扰,我们只想为给您带来一些惊喜...
多一份免费策划方案,总有益处。

请直接添加技术总监微信联系咨询

深正互联微信
扫描即可沟通