微信小程序自定义tabs切换,滑动动画

组件说明

开发项目的时候想着把所有tabs切换的功能做成组件,方便调用
功能满足项目需求就行,没有做太多的功能。使用的时候可自行增加更多功能
主要功能有
(1)是否横向滚动
(2)css样式自定义
(3)默认选中:下标0开始

组件代码非常简洁。

大致思路就是
1、在组件生命周期ready中进行赋值选中操作和滑块的位置计算。
2、点击切换tab的时候进行四步操作,(1)赋值选中项;(2)计算滑块位置;(3)计算滚动条位置;(4)绑定切换事件this.triggerEvent()

自定义组件

创建components目录
在components目录新建tabs目录
在tabs目录新建四个文件
js json wxml wxss
在这里插入图片描述

js文件

// getAllRect, getRect, nextTicks 这三个看下面的注释。
/*
export function nextTick(cb) {
     if (wx.canIUse('nextTick')) {
         wx.nextTick(cb);
     }
     else {
         setTimeout(() => {
             cb();
         }, 1000 / 30);
     }
 }
export function getRect(context, selector) {
    return new Promise((resolve) => {
        wx.createSelectorQuery()
            .in(context)
            .select(selector)
            .boundingClientRect()
            .exec((rect = []) => resolve(rect[0]));
    });
}
export function getAllRect(context, selector) {
    return new Promise((resolve) => {
        wx.createSelectorQuery()
            .in(context)
            .selectAll(selector)
            .boundingClientRect()
            .exec((rect = []) => resolve(rect[0]));
    });
}
*/
import { getAllRect, getRect, nextTick } from '../../js/utils';
Component({
     properties: {
          list: {
               type: Array,
               value: []
          },
          customStyle: {
               type: String,
               value: ''
          },
          active:{
               type:Number,
               value:0
          },
          scrollable: {
               type: Boolean,
               value: true
          }
     },
     data: {
          scrollLeft: 0,
          currentIndex: 0,
          lineOffsetLeft: 0,
          lineWidth: 40
     },
     lifetimes: {
     //在组件在视图层布局完成后执行
          ready: function () {
               let that = this;
               this.setData({
                    currentIndex:this.data.active,
                    scrollable: this.data.list.length <= 5 ? false : true
               }, function () {
                    const currentIndex = that.data.currentIndex;
                    Promise.all([
                         getAllRect(this, '.cl-tab'),
                         getRect(this, '.cl-tabs__line'),
                    ]).then(([rects = [], lineRect]) => {
                         const rect = rects[currentIndex];
                         if (rect == null) {
                              return;
                         }
                         let lineOffsetLeft = rects.slice(0, currentIndex).reduce((prev, curr) => prev + curr.width, 0);
                         lineOffsetLeft += (rect.width - lineRect.width) / 2;
                         that.setData({
                              lineOffsetLeft
                         });
                    });
               });
          }
     },
     methods: {
     //切换组件
          swich: function (e) {
               const { data } = this;
               const currentIndex = e.currentTarget.dataset.index;
               if (currentIndex === data.currentIndex) {
                    return;
               }
               const shouldEmitChange = data.currentIndex !== null;
               this.setData({ currentIndex });
               nextTick(() => {
                    this.resize(false);
                    this.scrollIntoView();
                    if (shouldEmitChange) {
                    //绑定事件到change
                         this.triggerEvent('change', {
                              index: currentIndex
                         })
                    }
               });
          },
          resize() {
               const { currentIndex } = this.data;
               Promise.all([
                    getAllRect(this, '.cl-tab'),
                    getRect(this, '.cl-tabs__line'),
               ]).then(([rects = [], lineRect]) => {
                    const rect = rects[currentIndex];
                    if (rect == null) {
                         return;
                    }
                    let lineOffsetLeft = rects.slice(0, currentIndex).reduce((prev, curr) => prev + curr.width, 0);
                    lineOffsetLeft += (rect.width - lineRect.width) / 2;
                    this.setData({
                         lineOffsetLeft
                    });
               });
          },
          scrollIntoView() {
               const { currentIndex, scrollable } = this.data;
               if (!scrollable) {
                    return;
               }
               Promise.all([
                    getAllRect(this, '.cl-tab'),
                    getRect(this, '.cl-tabs-nav'),
               ]).then(([tabRects, navRect]) => {

                    const tabRect = tabRects[currentIndex];
                    const offsetLeft = tabRects
                         .slice(0, currentIndex)
                         .reduce((prev, curr) => prev + curr.width, 0);
                    this.setData({
                         scrollLeft: offsetLeft - (navRect.width - tabRect.width) / 2,
                    });
               });
          }
     }
})


json文件

//启用自定义插件
{
     "component": true
}

wxml文件

<--  
	customStyle 自定义css样式 
	scrollable 是否横向滚动
	lineOffsetLeft 当前选择的线条偏移量
	lineWidth 线条的宽度
-->
<view class="cl-tabs"  style="{{customStyle}}">
     <scroll-view scroll-x="{{ scrollable }}" scroll-with-animation class="" scroll-left="{{ scrollLeft }}">
          <view class="cl-tabs-nav {{scrollable ? 'cl-tabs-scroll' : 'cl-tabs-noscroll'}}">
               <view class="cl-tabs__line"
                    style="width:{{lineWidth}}px;transform:translateX({{lineOffsetLeft}}px);-webkit-transform:translateX({{lineOffsetLeft}}px);transition-duration:0.3s;-webkit-transition-duration:0.3s;">
               </view>
               <view wx:for="{{list}}" data-index="{{index}}" class="cl-tab {{currentIndex==index ? 'cl-tab-active' : ''}}"
                    catchtap="swich">{{item.title}}
               </view>
          </view>
     </scroll-view>
</view>

wxss文件

.cl-tabs{
     background-color: #ffffff;
}

.cl-tabs-scroll {
     position: relative;
     user-select: none;
     white-space: nowrap;
}
.cl-tabs-noscroll{
     display: -webkit-flex;
     display: flex;
     overflow: hidden;
     
}

.cl-tab {
     position: relative;
     min-width: 0;
     padding: 0 20rpx;
     text-align: center;
     cursor: pointer;
     color: #000000;
     font-size: 28rpx;
     line-height: 80rpx;
}
.cl-tabs-scroll .cl-tab{
     display: inline-block;
     padding:0 30rpx;
}
.cl-tabs-noscroll .cl-tab{
     flex: 1;
}
.cl-tab-active{
     font-weight: 500;
     color:#f63434;
}
.cl-tabs__line {
     position: absolute;
     bottom: 0;
     left: 0;
     z-index: 1;
     height: 6rpx;
     background-color: #f63434;
}

如何使用

json文件中引用
在json文件引用插件

wxml文件中使用
list数组

//有title属性就行
//例如
[{title:'tab1'},{title:'tab2'}]

有title属性就行,其他根据实际情况使用

//wxml切换事件 bind:change="tabsChange"
//在js文件中通过detail.index获取当前tab的位置 从0开始
 tabsChange:function(e){
        console.log(e.detail.index);
 }

在wxml页面使用自定义组件tabs

效果演示

不滚动

在这里插入图片描述

横向滚动

使用组件的时候将scrollable设置为true或者tab超过5个会自动变更为横向滚动。

在这里插入图片描述

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

澜清___

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值