美文网首页
Taro实现 微信小程序 可左右滑动切换的Tab组件

Taro实现 微信小程序 可左右滑动切换的Tab组件

作者: R_X | 来源:发表于2022-10-27 16:19 被阅读0次
    image.png

    左右滑动切换Tab

    一、文件组成:

    • BgTitleTouchGroup.vue
    • BgTitleTouchGroup.less
    • BgTitleTouchItem.vue
    • BgTitleTouchItem.less
    • announcement.vue
    1、BgTitleTouchGroup.vue
    <template>
        <!-- 可以滑屏切换的 tabGroup -->
        <view class="tab-group">
            <!-- 标题列表 -->
            <scroll-view
                id="bgTitleTouchGroup"
                :scroll-x="true"
                :scroll-into-view="`bgg${activeIndex}`"
                :scroll-with-animation="true"
                class="bg-title-list flex f-ai-c"
                :class="[align]"
            >
                <view
                    v-for="(item, index) in newTabList"
                    :id="`bgg${index}`"
                    :key="index"
                    class="tab-item-box"
                >
                    <BgTitle
                        v-if="item.active"
                        :title="item.title"
                        :fontSize="30"
                    />
                    <text
                        v-else
                        class="normal"
                        :style="{
                            minWidth: normalMinWidth
                        }"
                        @tap="clickHandle(index)"
                    >
                        {{ item.title }}
                    </text>
                </view>
            </scroll-view>
            <!-- </view> -->
            <!-- 内容容器 -->
            <view
                class="content"
                :style="{'transition-duration': `${duration}s`, transform: `translateX(${xPositon}%)`}"
                @touchstart="onTouchStart"
                @touchmove="onTouchMove"
                @touchend="onTouchEnd"
            >
                <view class="tabs__track flex">
                    <slot />
                </view>
            </view>
        </view>
    </template>
    
    <script>
    import './BgTitleTouchGroup.less';
    
    import BgTitle from '../BgTitle/index.vue';
    import { getDirection, resetTouchStatus } from '@/combination';
    
    export default {
        name: 'BgTitleTouchGroup',
        components: { BgTitle },
        inject: {
            normalMinWidth: {
                from: 'normalMinWidth',
                // default: '110rpx'
                default: 'auto'
            }
        },
        props: {
            // 过度动画的时间
            duration: {
                type: Number,
                default: 0.2
            },
            align: {
                type: String,
                default: 'f-jc-sb'
            }
        },
        data () {
            return {
                activeIndex: 0, // 当前查看的tab的索引
                newTabList: [],
                xPositon: 0, // 容器在X轴移动的距离
                swipeable: true,
                swiping: false,
                direction: '',
                deltaX: 0,
                deltaY: 0,
                offsetX: 0,
                offsetY: 0,
                startX: 0,
                startY: 0
            };
        },
        created () {
            this.initTabList();
        },
        methods: {
            setActiveIndex (index) {
                this.activeIndex = index;
                for (let i = 0; i < this.newTabList.length; i++) {
                    if (i === index) {
                        this.newTabList[i].active = true;
                    } else this.newTabList[i].active = false;
                }
            },
            initTabList () {
                let slots = this.$slots.default();
                // for循环方式添加的 tabItem, 反之则为逐个写入的tabItem
                if (slots.length === 1 && typeof slots[0].type === 'symbol') {
                    slots = slots[0].children;
                }
                for (let i = 0; i < slots.length; i++) {
                    this.newTabList.push({
                        title: slots[i].props.title,
                        active: !i
                    });
                }
            },
            clickHandle (index) {
                for (let i = 0; i < this.newTabList.length; i++) {
                    if (i === index) this.newTabList[i].active = true;
                    else this.newTabList[i].active = false;
                }
                this.xPositon = -100 * index;
                this.activeIndex = index;
                this.$emit('bgTitleClick', index);
            },
            touchStart (event) {
                resetTouchStatus(this);
                var touch = event.touches[0];
                this.startX = touch.clientX;
                this.startY = touch.clientY;
            },
            touchMove (event) {
                var touch = event.touches[0];
                this.deltaX = touch.clientX - this.startX;
                this.deltaY = touch.clientY - this.startY;
                this.offsetX = Math.abs(this.deltaX);
                this.offsetY = Math.abs(this.deltaY);
                this.direction = this.direction || getDirection(this.offsetX, this.offsetY);
            },
            onTouchStart (event) {
                if (!this.swipeable) { return; }
                this.swiping = true;
                this.touchStart(event);
            },
            onTouchMove (event) {
                if (!this.swipeable || !this.swiping) { return; }
                this.touchMove(event);
            },
            // watch swipe touch end
            onTouchEnd () {
                if (!this.swipeable || !this.swiping) { return; }
                var _a = this; var direction = _a.direction; var deltaX = _a.deltaX; var offsetX = _a.offsetX;
                var minSwipeDistance = 50;
                if (direction === 'horizontal' && offsetX >= minSwipeDistance) {
                    this.activeIndex = this.getAvaiableTab(deltaX);
                    const list = this.newTabList.map((l, ind) => {
                        l.active = ind === this.activeIndex;
                        return l;
                    });
                    this.newTabList = list;
                    this.xPositon = -100 * this.activeIndex;
                    this.$emit('bgTitleClick', this.activeIndex);
                }
                this.swiping = false;
            },
            getAvaiableTab (direction) {
                var _a = this;
                var tabs = _a.newTabList;
                var currentIndex = _a.activeIndex;
                var step = direction > 0 ? -1 : 1;
                for (var i = step; currentIndex + i < tabs.length && currentIndex + i >= 0; i += step) {
                    var index = currentIndex + i;
                    if (index >= 0 && index < tabs.length && tabs[index] && !tabs[index].active) {
                        return index;
                    }
                }
                return 0;
            }
        }
    };
    </script>
    
    2、BgTitleTouchGroup.less
    .tab-group {
        width: 100%;
        overflow: hidden;
        .bg-title-list {
            white-space: nowrap;
            .normal {
                display: inline-block;
                font-size: 26rpx;
            }
            .tab-item-box {
                display: inline-block;
                margin-right: 61rpx;
            }
            .tab-item-box:last-child {
                margin-right:0;
            }
        }
        .content {
            .tabs__track {
                position: relative;
                width: 100%;
                height: 100%;
                will-change: left;
            }
        }
    }
    
    3、BgTitleTouchItem.vue
    <template>
        <!-- 可以滑屏切换的 tabItem -->
        <view class="tab-item">
            <slot />
        </view>
    </template>
    
    <script>
    import './index.less';
    
    export default {
        name: 'BgTitleTouchItem',
        props: {
            title: {
                type: String,
                default: '标题1'
            }
        }
    };
    </script>
    
    4、BgTitleTouchItem.less
    .tab-item {
        width: 100%;
        flex-shrink: 0;
        box-sizing: border-box;
    }
    .tab-item_inactive {
        height: 0;
        overflow: visible;
    }
    
    5、announcement.vue
    <template>
        <view>
            <BgTitleTouchGroup
                v-if="tabList.length"
                :align="'f-jc-fs'"
                @bgTitleClick="bgTitleClickHandle"
            >
                <BgTitleTouchItem
                    v-for="(ite, ind) in tabList"
                    :key="ite.id"
                    :title="ite.typeName"
                >
                    <view v-if="activeTypeIndex === ind">
                        <scroll-view
                            v-if="recodes.length"
                            :scroll-y="true"
                            class="announs"
                            @scrolltolower="onTolowerMixin(getRecodeList)"
                                >
                            <CommonListItem
                                v-for="item in recodes"
                                :key="item.id"
                                :record="item"
                                style="margin-bottom: 30rpx;"
                                @equiClick="clickHandle(item.id)"
                            />
                        </scroll-view>
                        <NoData
                            v-else
                            style="margin-top: 20rpx;"
                        />
                    </view>
                </BgTitleTouchItem>
            </BgTitleTouchGroup>
            <NoData
                v-else
                style="margin-top: 20rpx;"
            />
        </view>
    </template>
    <script>
        import {
            BgTitleTouchGroup,
            BgTitleTouchItem,
            CommonListItem,
            NoData
        } from '@/components';
    export default {
        name: 'Announcement',
        components: {
            BgTitleTouchGroup,
            BgTitleTouchItem,
            CommonListItem,
            NoData
        },
        data () {
            return {
                tabList: [],
                recodes: [],
                activeTypeId: '',
                activeTypeIndex: 0
            };
        },
        methods: {
            // 某个类型title点击
            bgTitleClickHandle (index) {
                this.activeTypeId = this.tabList[index].id;
                this.activeTypeIndex = index;
                this.initRecords();
                this.getRecodeList();
            },
        }
    };
    </script>
    

    相关文章

      网友评论

          本文标题:Taro实现 微信小程序 可左右滑动切换的Tab组件

          本文链接:https://www.haomeiwen.com/subject/dcsttdtx.html