Use a scroll-view component with horizontal scrolling to create the tab navigation bar. This enables the tabs to be swipealbe when they exceed the viewport width.
Maintain synchronization between the active tab header and its corresponding content panel by binding both to a shared state variable. This variable controls the active index for the navigation and the current item for the content swiper.
Calculate dynamic veiwport height for content areas by subtracting fixed layout heights from the total available screen space. This ensures scrollable content fits within the remaining area.
Structure (WXML)
<view class="container">
<view class="section-title">Swipeable Tab Demo</view>
<view class="tab-section">
<scroll-view scroll-x="true" class="nav-scroller" scroll-left="{{navScrollPosition}}">
<view class="nav-btn {{activeIndex==0?'selected':''}}" data-idx="0" bindtap="selectTab">Tab One</view>
<view class="nav-btn {{activeIndex==1?'selected':''}}" data-idx="1" bindtap="selectTab">Tab Two</view>
</scroll-view>
<swiper class="content-panel" current="{{activeIndex}}" duration="300" bindchange="handleSwipe" style="height:{{contentHeight}}rpx">
<swiper-item>
<scroll-view scroll-y="true" class="scroll-panel">
<view class="content-heading">
Content for Tab One
</view>
</scroll-view>
</swiper-item>
<swiper-item>
<scroll-view scroll-y="true" class="scroll-panel">
<view class="content-heading">
Content for Tab Two
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</view>
Styling (WXSS)
.container{
padding: 30rpx;
}
.container .section-title{
color: #2a7de1;
font-size: 15px;
height: 100rpx;
line-height: 100rpx;
}
.container .nav-scroller{
border-bottom: 2rpx solid #2a7de1;
}
.container .nav-btn{
font-size: 15px;
display:inline-block;
width: 150rpx;
margin-left: 20rpx;
background: #e6f0fc;
color: #2a7de1;
height: 70rpx;
line-height: 70rpx;
text-align: center;
border-radius: 8rpx 8rpx 0 0;
}
.container .selected{
background: #2a7de1;
color: #ffffff;
}
.content-panel swiper-item view{
padding: 30rpx;
font-size: 15px;
}
.scroll-panel {
height: 100%;
}
Logic (JS)
Page({
data: {
contentHeight: "",
activeIndex: 0,
navScrollPosition: 0,
},
// Handler for swiper change event
handleSwipe: function (swiperEvent) {
this.setData({
activeIndex: swiperEvent.detail.current
});
this.adjustNavScroll();
},
// Handler for tab click event
selectTab: function (tapEvent) {
let tappedIndex = tapEvent.currentTarget.dataset.idx;
if (this.data.activeIndex === tappedIndex) { return; }
this.setData({
activeIndex: tappedIndex
});
},
// Adjust navigation scroll position based on active tab
adjustNavScroll: function () {
let scrollPos = this.data.activeIndex > 3 ? 250 : 0;
this.setData({
navScrollPosition: scrollPos
});
},
onLoad: function () {
let pageContext = this;
wx.getSystemInfo({
success: function (sysInfo) {
let viewHeight = sysInfo.windowHeight;
let viewWidth = sysInfo.windowWidth;
let rpxRatio = 750 / viewWidth;
// 500 represents the combined height of fixed elements (headers, nav, etc.)
let calculatedHeight = viewHeight * rpxRatio - 500;
pageContext.setData({
contentHeight: calculatedHeight
});
}
});
}
})