前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小程序 tab 滚动列表优化方案

小程序 tab 滚动列表优化方案

作者头像
前端开发博客
发布2020-11-04 11:33:14
2K0
发布2020-11-04 11:33:14
举报
文章被收录于专栏:前端开发博客前端开发博客

类似于今日头条资讯切换列表

今天在做百度小程序的转换,发现真机上用之前的swiper-item结合scroll-view 实现的Tab列表的效果不理想,于是我重新思考,发现了一种更合适的方案。

之前的缺陷

  1. swiper-item里面的内容使用的是view组件,导致每次切换到新的swiper-item时,历史定位都重置了。这样导致了我每次切换到另外一个swiper-item时要计算他的滚动位置和他的全部元素高度。
  2. 我还需要频繁记录每次滚动的定位,保存起来,以便下次用的时候来拿,使用scroll事件很卡。
  3. 上面返回历史位置时,最外层的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:

代码语言:javascript
复制
<!--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:

代码语言:javascript
复制
// 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
    };
  }
});
代码语言:javascript
复制
/* 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;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端开发博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 之前的缺陷
  • 解决方法
  • 代码如下
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档