Taro實(shí)現(xiàn) 微信小程序 可左右滑動切換的Tab組件

image.png

左右滑動切換Tab

一、文件組成:

  • BgTitleTouchGroup.vue
  • BgTitleTouchGroup.less
  • BgTitleTouchItem.vue
  • BgTitleTouchItem.less
  • announcement.vue
1溶浴、BgTitleTouchGroup.vue
<template>
    <!-- 可以滑屏切換的 tabGroup -->
    <view class="tab-group">
        <!-- 標(biāo)題列表 -->
        <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> -->
        <!-- 內(nèi)容容器 -->
        <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, // 當(dāng)前查看的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循環(huán)方式添加的 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: '標(biāo)題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點(diǎn)擊
        bgTitleClickHandle (index) {
            this.activeTypeId = this.tabList[index].id;
            this.activeTypeIndex = index;
            this.initRecords();
            this.getRecodeList();
        },
    }
};
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末国夜,一起剝皮案震驚了整個濱河市米愿,隨后出現(xiàn)的幾起案子厦凤,更是在濱河造成了極大的恐慌,老刑警劉巖育苟,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件较鼓,死亡現(xiàn)場離奇詭異,居然都是意外死亡违柏,警方通過查閱死者的電腦和手機(jī)博烂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漱竖,“玉大人脖母,你說我怎么就攤上這事∠泄拢” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵烤礁,是天一觀的道長讼积。 經(jīng)常有香客問我,道長脚仔,這世上最難降的妖魔是什么勤众? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮鲤脏,結(jié)果婚禮上们颜,老公的妹妹穿的比我還像新娘吕朵。我一直安慰自己,他們只是感情好窥突,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布努溃。 她就那樣靜靜地躺著,像睡著了一般阻问。 火紅的嫁衣襯著肌膚如雪梧税。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天称近,我揣著相機(jī)與錄音第队,去河邊找鬼。 笑死刨秆,一個胖子當(dāng)著我的面吹牛凳谦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衡未,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼尸执,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眠屎?” 一聲冷哼從身側(cè)響起剔交,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎改衩,沒想到半個月后岖常,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葫督,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年竭鞍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橄镜。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡偎快,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洽胶,到底是詐尸還是另有隱情晒夹,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布姊氓,位于F島的核電站丐怯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏翔横。R本人自食惡果不足惜读跷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禾唁。 院中可真熱鬧效览,春花似錦无切、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矛洞,卻和暖如春洼哎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沼本。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工噩峦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抽兆。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓识补,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辫红。 傳聞我的和親對象是個殘疾皇子凭涂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容