微信小程序实现滑动选项卡
关于“如何做小程序的滑动式Tab选项卡,方法是什么”, 有一些人不是很理解,今天简单实现一下小程序的滑动选项卡。
效果如下图所示:

这种设计结构其实在移动端是非常常见的设计,无论是h5
, app
还是 小程序
,今天我们就用微信小程序来实现这个功能组件。
首先我们可以把这个分成上下两部分结构,上面可以点击切换的 tab
标签和下面能切换的视图。
首先我们分析上面,其实对于前端同学来说实现上面部分有很多方式,无非就是几个标签,激活的那个标签加个样式而已,但是我们这里要多考虑如果tab
较多或者标签的字数比较长,做过多语言的同学可能知道,中文翻译成其他各种外语的时候,有些单词可能会很长,所以我们在设计的时候考虑到这种场景,我们最好把上面tab
的设计成可以滑动的,这样无论多长或者多少tab
我们都不怕我们的布局会打乱。所以我们可以用小程序的 scroll-view
来实现上面的布局。
然后我们接着看下面,是不是很眼熟,没错,就是我们常见的轮播图结构。我们可以用swiper
来实现视图区域,知道了怎么布局,接下来我们只需要将上下两部分联动起来就ok了。
我们只需要在项目的 components
文件夹下新建一个 scroll-tab-view
的组件(RN
同款的组件也叫这个名字,暂且也就叫这个吧),然后我们来实现布局部分:
<!--components/scroll-tab-view/index.wxml-->
<view>
<!-- 顶部tab -->
<scroll-view scroll-x="true" enable-flex="true" class="scroll_h" scroll-left="{{scrollLeft}}" >
<view
wx:for="{{tabs}}"
wx:key="item"
data-current="{{index}}"
style="width: {{tabWidth + 'px'}}"
class="['scroll_item', {{currentIndex === index ? 'active' : ''}}]"
mut-bind:tap="changeTabs">
<view class="tabItem" style="width: {{tabWidth + 'px'}}">
<text>{{item}}</text>
</view>
</view>
<!-- 选中的下划线 -->
<view class="select_line" style="width: {{tabWidth + 'px'}}; left: {{(currentIndex * tabWidth) + 'px'}}"></view>
</scroll-view>
<!-- tab对应视图区域 -->
<view class="scroll_content">
<swiper class="tab_content" duration="300" current="{{currentIndex}}" bindchange="switchTabView">
<swiper-item wx:for="{{tabs}}" wx:key="item">
<scroll-view
scroll-y="true"
class="scoll_h"
refresher-enabled="true"
refresher-default-style="black"
refresher-threshold="{{80}}"
refresher-triggered="{{isRefresh}}"
refresher-background="#c5c5c5"
bindrefresherrefresh="onRefresh"
>
<view class="content_view">
<!-- 此处循环渲染插槽,用来显示我们tab对应的列表内容 -->
<slot name="{{index}}"></slot>
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</view>
我们把选中时的下划线加上动画,让它看起来更加的丝滑,而不是直接通过 active
样式来修改border
来实现。然后我们来给它加上样式。
/* components/scroll-tab-view/index.wxss */
.scroll_h {
position: relative;
display: flex;
height: 40px;
line-height: 40px;
border-bottom: 0.5px solid #e4e4e4;
}
.scroll_item {
height: 40px;
display: inline-block;
}
.tabItem {
display: flex;
justify-content: center;
/* width: 80px; */
height: 50rpx;
}
.active {
/* border-bottom: 6rpx solid #0d7eff; */
color: #0d7eff;
font-weight: 600;
}
.select_line {
position: absolute;
bottom: 20px; // 根据自己情况定位选中线位置
height: 6rpx;
background-color: #0d7eff;
transition: left 0.2s ease-in-out;
}
.scroll_content {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 41px;
}
.tab_content {
height: 100%;
}
.scoll_h {
height: 100%;
}
.content_view {
height: 100%;
padding: 32rpx;
background: #f1f1f1;
}
然后实现js部分:
// components/scroll-tab-view/index.js
Component({
options:{
multipleSlots:true //这样就可以设置多个slot插槽
},
/**
* 组件的属性列表
*/
properties: {
// tab选项卡标签的配置列表
tabs: {
type: Array,
value: []
},
currentIndex: { // 当前选中的tab index
type: [Number, String],
value: 0
},
tabWidth: { // 每个tab设置的宽度
type: Number,
value: 80
},
isRefresh: {// 当前下拉刷新状态
type: Boolean,
value: false
}
},
/**
* 组件的初始数据
*/
data: {
scrollLeft: 0, // tab标题滚动条位置
},
/**
* 组件的方法列表
*/
methods: {
changeTabs(e) {
const currentIndex = e.currentTarget.dataset.current;
this.triggerEvent('ChangeTab', {currentIndex});
this.checkScrollBar(currentIndex);
},
// 切换视图
switchTabView(e) {
console.log('switchTabView', e);
const currentIndex = e.detail.current;
this.triggerEvent('ChangeTab', { currentIndex });
this.checkScrollBar(currentIndex);
},
// 设置滚动条位置
checkScrollBar(currentIndex) {
// 获取屏幕宽度
const { screenWidth } = wx.getSystemInfoSync();
// 计算出完全显示在屏幕区的tab个数
const $num = parseInt(screenWidth / this.data.tabWidth);
if(currentIndex + 1 > $num) {
this.setData({
scrollLeft: $num * this.data.tabWidth
})
} else {
this.setData({
scrollLeft: 0
})
}
},
// 触发下拉刷新
onRefresh() {
this.triggerEvent('refresh', {
current: this.data.currentIndex,
})
}
}
})
这样我们就已经完成了我们组件的设计和实现,下面我们用该组件来实现我们上面图中的页面功能。
<!--pages/news/index.wxml-->
<view class="my_order">
<scroll-tab-view
tabs="{{tabs}}"
currentIndex="{{currentIndex}}"
isRefresh="{{isRefresh}}"
bindChangeTab="changeTab"
bindrefresh="refresh">
<!-- 视图1 -->
<view slot="0" class="list_contain">
<view wx:for="{{list}}" wx:key="id" class="list_item">
<view>{{item.title}}</view>
<view class="list_date">{{item.date}}</view>
<view class="list_content">
<view class="list_img">
<image src="{{item.url}}"></image>
</view>
<text>{{item.content}}</text>
</view>
</view>
</view>
<!-- 视图2 -->
<view slot="1" class="list_contain">
<!-- ... -->
</view>
<!-- 视图3 -->
<view slot="2" class="list_contain">
<!-- ... -->
</view>
<!-- 视图4 -->
<view slot="3" class="list_contain">
<!-- ... -->
</view>
</scroll-tab-view>
</view>
js:
// pages/news/index.js
Page({
/**
* 页面的初始数据
*/
data: {
tabs: ['健康', '情感', '职场', '育儿', '财经', '其他'],
currentIndex: 0, // 当前选中的tab
list: [], // 数据源
showLoading: false, // loading
isRefresh: false // 下拉刷新状态
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getNewsList();
},
/**
* 生命周期函数--监听页面显示
* 自定义tabbar的部分,可以忽略
*/
onShow: function () {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
const tabBar = this.getTabBar();
let roleId = wx.getStorageSync('role');
let selected = roleId ? 1 : 0;
tabBar.setData({
selected
})
}
},
// 切换tab,保存当前选中的index
changeTab(e) {
const { currentIndex } = e.detail;
this.setData({ currentIndex });
},
// 获取列表数据
getNewsList() {
let _this = this;
wx.request({
url: 'https://miniapp.com/getNewsList',
success(res) {
const { list } = Reflect.get(res, 'data');
_this.setData({
showLoading: false,
isRefresh: false,
list
});
},
fail(e) {
wx.showToast({
title: e.errMsg,
})
_this.setData({ isRefresh: false })
}
})
},
/*
* 列表下拉刷新(忽略)
*/
refresh() {
this.getNewsList();
}
})
css:
/* pages/myOrder/myOrder.wxss */
.my_order {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: calc(env(safe-area-inset-bottom) + 48px);
}
.list_contain {
height: 100%;
}
.list_item {
display: flex;
flex-direction: column;
border-radius: 5px;
background: #fff;
margin-bottom: 10px;
padding: 8px 16px;
}
.list_img {
width: 80px;
height: 50px;
margin-right: 16px;
}
.list_img image {
width: 80px;
height: 50px;
}
.list_date {
font-size: 24rpx;
color: #aaaaaa;
}
.list_content {
display: flex;
flex-direction: row;
margin-top: 20px;
}
json文件:
// 引入我们的组件
{
"usingComponents": {
"scroll-tab-view": "../../components/scroll-tab-view"
},
"navigationBarTitleText": "资讯"
}
组件就开发完成了,我们引入组件后要做的,只是在对应tab
下面的 slot
中渲染我们自己的页面内容即可。