微信小程序滑动标签栏tabs+自适应swiper

废话不多说,先上效果图

在这里插入图片描述

1. 先新建一个用来存放tabs的文件

在这里插入图片描述

tabs.wxml
<scroll-view scroll-x="{{true}}" class="scroll" scroll-left="{{transscr}}" scroll-with-animation="{{true}}">
    <block wx:for="{{items}}" wx:key="_this">
        <view class="scroll_item" style="{{idx==index?'font-weight: bold;transform: scale(1.1);':''}}" bind:tap="switchover" data-index="{{index}}">{{item.name}}</view>
    </block>
    <view class="scroll_glide" style="width:{{transwidth}}px;transform: translateX({{transx}}px);">
        <view class="scroll_view"></view>
    </view>
</scroll-view>
tabs.js
Component({
  /**
   * 组件的初始数据
   */
  data: {
    // 子元素的距离
    transx: 0,
    // 设置宽度
    transwidth: 0,
    // 子元素距离左边位置
    element: [],
    // 子元素宽度
    element_width: [],
    // 距离左边
    transscr: 0
  },
  // 父组件传值
  properties: {
    items: null,
    idx: {
      type: Number,
      value: 0,
      observer: function (newVal, oldVal, changedPath) {
        let newlist = {
          currentTarget: {
            dataset: {
              index: newVal
            }
          }
        }
        this.switchover(newlist);
      }
    }
  },
  /**
   * 组件的初始数据方法
   */
  ready() {
    let _this = this;
    let _list = [];
    let _width = [];
    _this.createSelectorQuery().selectAll('.scroll_item').boundingClientRect(function (rects) {
      rects.forEach(function (rect) {
        _list.push(rect.left - 12); //这里减12是因为我在css里写了一个padding-left:24rpx;
        _width.push(rect.width);
      })
      _this.setData({
        element: _list,
        transwidth: _width[0],
        element_width: _width
      })
    }).exec();
  },
  /**
   * 组件的方法列表
   */
  methods: {
    switchover({
      currentTarget
    }) {
      // 获取子元素距离左边的距离的集合
      let distance = this.data.element;
      // 获取子元素的宽度
      let width = this.data.element_width;
      // 获取索引
      let idx = currentTarget.dataset.index;
      // 获取屏幕宽度
      let screenHalfWidth = this.data.screenHalfWidth;
      // 赋值
      this.setData({
        transx: distance[idx],
        transwidth: width[idx],
        transscr: distance[idx] - (width[idx] / 2),
        idx: idx
      })
      // 给父组件传值
      this.triggerEvent('parentEvent', {
        index: idx
      })
    }
  }
})
当子组件需要传值的时候就需要在子组件的json里加入
 "component": true
tabs.wxss
.scroll {
    height: 80rpx;
    width: 750rpx;
    background: #ffffff;
    white-space: nowrap;
    padding-left: 24rpx;
    box-sizing: border-box;
}

.scroll_item {
    display: inline-flex;
    margin-right: 30rpx;
    height: 70rpx;
    line-height: 70rpx;
    font-size: 26rpx;
    transition: .3s;
}

.scroll_view {
    width: 30rpx;
    height: 6rpx;
    background: #12299e;
}
.scroll_glide{
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    transition: .3s;
}

2. 然后我们在父组件中调取这个组件

   "usingComponents": {
    "z-tabs": "../../component/tabs/tabs"
  }
父组件wxml中写入
<view class="mer_tabs">
    <z-tabs items="{{tab_items}}" idx="{{tab_idx}}" bind:parentEvent='onTapChild'></z-tabs>
</view>
父组件的css
.mer_tabs {
    position: sticky; /*这里是让他漂浮在上面的*/
    top: 0;
    z-index: 99;
}
父组件的js
data:{
	//tabs
    tab_items: [],
    tab_idx: 0 //因为一会要写自适应的swiper
}
// 子组件传值了
  onTapChild({ detail }) {
    let list = this.data.arrlist_height //这里主要是用来适配下面swiper的,如若不用,删了即可
    this.setData({
      tab_idx: detail.index,
      arrheight: list[detail.index]
    })
  },

3.swiper的自适应

<!-- 底部列表 -->
<swiper style="height:{{arrheight}}px" bindanimationfinish='changeGoodsSwip' bindchange="SwiperChange" current="{{tab_idx}}" duration="300">
    <swiper-item wx:for="{{tab_items}}" wx:key='_this' wx:for-item="tabs">
        <view class="lists">
            <block wx:for="{{array}}" wx:key="_this">
              <view></view> <!--这里写自己的内容就好了-->
            </block>
        </view>
    </swiper-item>
</swiper>
js中
 data: {
    // 底部列表
    array: ["", "", "", "", "", "", ""],
    // 获取底部高度进行自适应
    arrheight: 0,
    // 高度列表
    arrlist_height: []
  },
  onLoad: function (options) {
    let _this = this;
    let listheight = [];
    // 这里是获取.lists的高度后然后push进一个数组中去
    setTimeout(function () {
      wx.createSelectorQuery().selectAll('.lists').boundingClientRect(function (rects) {
        rects.forEach(res => {
          listheight.push(res.height)
        });
        _this.setData({
          arrheight: listheight[0],
          arrlist_height: listheight
        })
      }).exec();
    }, 500)
  },
  // 子组件传值了
  onTapChild({detail}) {
    let list = this.data.arrlist_height
    this.setData({
      tab_idx: detail.index,
      arrheight: list[detail.index]
    })
  },
  // 滑动改变了current
  SwiperChange(event) {
    let list = this.data.arrlist_height;
    this.setData({
      tab_idx: event.detail.current,
      arrheight: list[event.detail.current]
    })
  },
  // 这里是防止swiper卡死
  changeGoodsSwip(event) {
    if (event.detail.source == "touch") {
      if (event.detail.current == 0) {
        //有时候这算是正常情况,所以暂定连续出现3次就是卡了
        let swiperError = this.data.swiperError
        swiperError += 1
        this.setData({
          swiperError: swiperError
        })
        if (swiperError >= 3) { //在开关被触发3次以上
          this.setData({
            tab_idx: this.data.preIndex
          }); //,重置current为正确索引
          this.setData({
            swiperError: 0
          })
        }
      } else { //正常轮播时,记录正确页码索引
        this.setData({
          preIndex: event.detail.current
        });
        //将开关重置为0
        this.setData({
          swiperError: 0
        })
      }
    }
  }

附上所有代码

父组件

<view class="mer_tabs">
    <z-tabs items="{{tab_items}}" idx="{{tab_idx}}" bind:parentEvent='onTapChild'></z-tabs>
</view>
<!-- 底部列表 -->
<swiper style="height:{{arrheight}}px" bindanimationfinish='changeGoodsSwip' bindchange="SwiperChange" current="{{tab_idx}}" duration="300">
    <swiper-item wx:for="{{tab_items}}" wx:key='_this' wx:for-item="tabs">
        <view class="lists">
            <block wx:for="{{array}}" wx:key="_this">
              <view></view>
            </block>
        </view>
    </swiper-item>
</swiper>
const app = getApp();

Page({

  /**
   * 页面的初始数据
   */
  data: {
    //tabs
    tab_items: [],
    tab_idx: 0,
    // 底部列表
    array: ["", "", "", "", "", "", ""],
    // 获取底部高度进行自适应
    arrheight: 0,
    // 高度列表
    arrlist_height: []
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    let _this = this;
    let listheight = [];
    setTimeout(function () {
      wx.createSelectorQuery().selectAll('.lists').boundingClientRect(function (rects) {
        rects.forEach(res => {
          listheight.push(res.height)
        });
        _this.setData({
          arrheight: listheight[0],
          arrlist_height: listheight
        })
      }).exec();
    }, 500)
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },
  // 子组件传值了
  onTapChild({
    detail
  }) {
    let list = this.data.arrlist_height
    this.setData({
      tab_idx: detail.index,
      arrheight: list[detail.index]
    })
  },
  // 滑动改变了current
  SwiperChange(event) {
    let list = this.data.arrlist_height;
    this.setData({
      tab_idx: event.detail.current,
      arrheight: list[event.detail.current]
    })
  },
  changeGoodsSwip(event) {
    if (event.detail.source == "touch") {
      if (event.detail.current == 0) {
        //有时候这算是正常情况,所以暂定连续出现3次就是卡了
        let swiperError = this.data.swiperError
        swiperError += 1
        this.setData({
          swiperError: swiperError
        })
        if (swiperError >= 3) { //在开关被触发3次以上
          this.setData({
            tab_idx: this.data.preIndex
          }); //,重置current为正确索引
          this.setData({
            swiperError: 0
          })
        }
      } else { //正常轮播时,记录正确页码索引
        this.setData({
          preIndex: event.detail.current
        });
        //将开关重置为0
        this.setData({
          swiperError: 0
        })
      }
    }
  }
})
{
  "usingComponents": {
    "z-tabs": "../../component/tabs/tabs"
  }
}

子组件

<scroll-view scroll-x="{{true}}" class="scroll" scroll-left="{{transscr}}" scroll-with-animation="{{true}}">
    <block wx:for="{{items}}" wx:key="_this">
        <view class="scroll_item" style="{{idx==index?'font-weight: bold;transform: scale(1.1);':''}}" bind:tap="switchover" data-index="{{index}}">{{item.name}}</view>
    </block>
    <view class="scroll_glide" style="width:{{transwidth}}px;transform: translateX({{transx}}px);">
        <view class="scroll_view"></view>
    </view>
</scroll-view>
Component({
  /**
   * 组件的初始数据
   */
  data: {
    // 子元素的距离
    transx: 0,
    // 设置宽度
    transwidth: 0,
    // 子元素距离左边位置
    element: [],
    // 子元素宽度
    element_width: [],
    // 距离左边
    transscr: 0
  },
  // 父组件传值
  properties: {
    items: null,
    idx: {
      type: Number,
      value: 0,
      observer: function (newVal, oldVal, changedPath) {
        let newlist = {
          currentTarget: {
            dataset: {
              index: newVal
            }
          }
        }
        this.switchover(newlist);
      }
    }
  },
  /**
   * 组件的初始数据方法
   */
  ready() {
    let _this = this;
    let _list = [];
    let _width = [];
    _this.createSelectorQuery().selectAll('.scroll_item').boundingClientRect(function (rects) {
      rects.forEach(function (rect) {
        _list.push(rect.left - 12);
        _width.push(rect.width);
      })
      _this.setData({
        element: _list,
        transwidth: _width[0],
        element_width: _width
      })
    }).exec();
  },
  /**
   * 组件的方法列表
   */
  methods: {
    switchover({
      currentTarget
    }) {
      // 获取子元素距离左边的距离的集合
      let distance = this.data.element;
      // 获取子元素的宽度
      let width = this.data.element_width;
      // 获取索引
      let idx = currentTarget.dataset.index;
      // 获取屏幕宽度
      let screenHalfWidth = this.data.screenHalfWidth;
      // 赋值
      this.setData({
        transx: distance[idx],
        transwidth: width[idx],
        transscr: distance[idx] - (width[idx] / 2),
        idx: idx
      })
      // 给父组件传值
      this.triggerEvent('parentEvent', {
        index: idx
      })
    }
  }

})
{
  "component": true,
  "usingComponents": {}
}
.scroll {
    height: 80rpx;
    width: 750rpx;
    background: #ffffff;
    white-space: nowrap;
    padding-left: 24rpx;
    box-sizing: border-box;
}

.scroll_item {
    display: inline-flex;
    margin-right: 30rpx;
    height: 70rpx;
    line-height: 70rpx;
    font-size: 26rpx;
    transition: .3s;
}

.scroll_view {
    width: 30rpx;
    height: 6rpx;
    background: #12299e;
}
.scroll_glide{
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    transition: .3s;
}

可能有些BUG,暂时没测试到,希望测试到的提意见我会修改的

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值