mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-06 03:58:05 +08:00
[improvement] Tabs:support swipeable
and sticky
property (#1019)
This commit is contained in:
parent
696ae3c661
commit
e1b68de7fc
@ -3,7 +3,8 @@ import Page from '../../common/page';
|
|||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
tabs: [1, 2, 3, 4],
|
tabs: [1, 2, 3, 4],
|
||||||
tabsMore: [1, 2, 3, 4, 5, 6, 7, 8]
|
tabsMore: [1, 2, 3, 4, 5, 6, 7, 8],
|
||||||
|
scrollTop: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickDisabled(event) {
|
onClickDisabled(event) {
|
||||||
@ -25,5 +26,11 @@ Page({
|
|||||||
title: `点击标签 ${event.detail.index + 1}`,
|
title: `点击标签 ${event.detail.index + 1}`,
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onPageScroll(event) {
|
||||||
|
this.setData({
|
||||||
|
scrollTop: event.scrollTop
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -69,6 +69,20 @@
|
|||||||
</van-tabs>
|
</van-tabs>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="粘性布局">
|
||||||
|
<van-tabs sticky scroll-top="{{ scrollTop }}">
|
||||||
|
<van-tab
|
||||||
|
wx:for="1234"
|
||||||
|
wx:key="index"
|
||||||
|
title="{{ '标签' + item }}"
|
||||||
|
>
|
||||||
|
<view class="content">
|
||||||
|
{{ '内容' + item }}
|
||||||
|
</view>
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
<demo-block title="切换动画">
|
<demo-block title="切换动画">
|
||||||
<van-tabs animated>
|
<van-tabs animated>
|
||||||
<van-tab
|
<van-tab
|
||||||
@ -82,3 +96,17 @@
|
|||||||
</van-tab>
|
</van-tab>
|
||||||
</van-tabs>
|
</van-tabs>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="滑动切换">
|
||||||
|
<van-tabs swipeable>
|
||||||
|
<van-tab
|
||||||
|
wx:for="1234"
|
||||||
|
wx:key="index"
|
||||||
|
title="{{ '标签' + item }}"
|
||||||
|
>
|
||||||
|
<view class="content" style="height: 1000px;">
|
||||||
|
{{ '内容' + item }}
|
||||||
|
</view>
|
||||||
|
</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
</demo-block>
|
||||||
|
@ -111,6 +111,19 @@ Page({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 粘性布局
|
||||||
|
|
||||||
|
通过`sticky`属性可以开启粘性布局,粘性布局下,当 Tab 滚动到顶部时会自动吸顶
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-tabs sticky>
|
||||||
|
<van-tab title="标签 1">内容 1</van-tab>
|
||||||
|
<van-tab title="标签 2">内容 2</van-tab>
|
||||||
|
<van-tab title="标签 3">内容 3</van-tab>
|
||||||
|
<van-tab title="标签 4">内容 4</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
```
|
||||||
|
|
||||||
#### 切换动画
|
#### 切换动画
|
||||||
|
|
||||||
可以通过`animated`来设置是否启用切换tab时的动画。
|
可以通过`animated`来设置是否启用切换tab时的动画。
|
||||||
@ -124,6 +137,19 @@ Page({
|
|||||||
</van-tabs>
|
</van-tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 滑动切换
|
||||||
|
|
||||||
|
通过`swipeable`属性可以开启滑动切换标签页
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-tabs swipeable>
|
||||||
|
<van-tab title="标签 1">内容 1</van-tab>
|
||||||
|
<van-tab title="标签 2">内容 2</van-tab>
|
||||||
|
<van-tab title="标签 3">内容 3</van-tab>
|
||||||
|
<van-tab title="标签 4">内容 4</van-tab>
|
||||||
|
</van-tabs>
|
||||||
|
```
|
||||||
|
|
||||||
### Tabs API
|
### Tabs API
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
@ -137,6 +163,10 @@ Page({
|
|||||||
| line-width | 底部条宽度 (px) | `Number` | 与当前标签等宽 |
|
| line-width | 底部条宽度 (px) | `Number` | 与当前标签等宽 |
|
||||||
| swipe-threshold | 滚动阈值,设置标签数量超过多少个可滚动 | `Number` | `4` |
|
| swipe-threshold | 滚动阈值,设置标签数量超过多少个可滚动 | `Number` | `4` |
|
||||||
| animated | 是否使用动画切换 Tabs | `Boolean` | `false` |
|
| animated | 是否使用动画切换 Tabs | `Boolean` | `false` |
|
||||||
|
| swipeable | 是否开启手势滑动切换 | `Boolean` | `false` |
|
||||||
|
| sticky | 是否使用粘性定位布局 | `Boolean` | `false` |
|
||||||
|
| offset-top | 粘性定位布局下与顶部的最小距离,单位 px | `Number` | `0` |
|
||||||
|
| scroll-top | 页面的`scrollTop`,粘性布局下必须要传入,单位 px | `Number` | `0` |
|
||||||
|
|
||||||
### Tab API
|
### Tab API
|
||||||
|
|
||||||
@ -158,6 +188,7 @@ Page({
|
|||||||
| bind:click | 点击标签时触发 | index:标签索引,title:标题 |
|
| bind:click | 点击标签时触发 | index:标签索引,title:标题 |
|
||||||
| bind:change | 当前激活的标签改变时触发 | index:标签索引,title:标题 |
|
| bind:change | 当前激活的标签改变时触发 | index:标签索引,title:标题 |
|
||||||
| bind:disabled | 点击被禁用的标签时触发 | index:标签索引,title:标题 |
|
| bind:disabled | 点击被禁用的标签时触发 | index:标签索引,title:标题 |
|
||||||
|
| bind:scroll | 滚动时触发 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } |
|
||||||
|
|
||||||
### 外部样式类
|
### 外部样式类
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { VantComponent } from '../common/component';
|
import { VantComponent } from '../common/component';
|
||||||
|
import { touch } from '../mixins/touch';
|
||||||
|
|
||||||
type TabItemData = {
|
type TabItemData = {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@ -8,6 +9,8 @@ type TabItemData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
VantComponent({
|
VantComponent({
|
||||||
|
mixins: [touch],
|
||||||
|
|
||||||
relation: {
|
relation: {
|
||||||
name: 'tab',
|
name: 'tab',
|
||||||
type: 'descendant',
|
type: 'descendant',
|
||||||
@ -54,7 +57,17 @@ VantComponent({
|
|||||||
type: Number,
|
type: Number,
|
||||||
value: 4
|
value: 4
|
||||||
},
|
},
|
||||||
animated: Boolean
|
animated: Boolean,
|
||||||
|
sticky: Boolean,
|
||||||
|
offsetTop: {
|
||||||
|
type: Number,
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
swipeable: Boolean,
|
||||||
|
scrollTop: {
|
||||||
|
type: Number,
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
@ -62,7 +75,9 @@ VantComponent({
|
|||||||
lineStyle: '',
|
lineStyle: '',
|
||||||
scrollLeft: 0,
|
scrollLeft: 0,
|
||||||
scrollable: false,
|
scrollable: false,
|
||||||
trackStyle: ''
|
trackStyle: '',
|
||||||
|
wrapStyle: '',
|
||||||
|
position: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@ -74,7 +89,9 @@ VantComponent({
|
|||||||
color: 'setLine',
|
color: 'setLine',
|
||||||
lineWidth: 'setLine',
|
lineWidth: 'setLine',
|
||||||
active: 'setActiveTab',
|
active: 'setActiveTab',
|
||||||
animated: 'setTrack'
|
animated: 'setTrack',
|
||||||
|
scrollTop: 'onScroll',
|
||||||
|
offsetTop: 'setWrapStyle'
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
@ -231,6 +248,100 @@ VantComponent({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onTouchStart(event: Weapp.TouchEvent) {
|
||||||
|
if (!this.data.swipeable) return;
|
||||||
|
|
||||||
|
this.touchStart(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onTouchMove(event: Weapp.TouchEvent) {
|
||||||
|
if (!this.data.swipeable) return;
|
||||||
|
|
||||||
|
this.touchMove(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
// watch swipe touch end
|
||||||
|
onTouchEnd() {
|
||||||
|
if (!this.data.swipeable) return;
|
||||||
|
|
||||||
|
const { active, tabs } = this.data;
|
||||||
|
|
||||||
|
const { direction, deltaX, offsetX } = this;
|
||||||
|
const minSwipeDistance = 50;
|
||||||
|
|
||||||
|
if (direction === 'horizontal' && offsetX >= minSwipeDistance) {
|
||||||
|
if (deltaX > 0 && active !== 0) {
|
||||||
|
this.setActive(active - 1);
|
||||||
|
} else if (deltaX < 0 && active !== tabs.length - 1) {
|
||||||
|
this.setActive(active + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setWrapStyle() {
|
||||||
|
const { offsetTop, position } = this.data;
|
||||||
|
let wrapStyle;
|
||||||
|
|
||||||
|
switch (position) {
|
||||||
|
case 'top':
|
||||||
|
wrapStyle = `
|
||||||
|
top: ${offsetTop}px;
|
||||||
|
position: fixed;
|
||||||
|
`;
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
wrapStyle = `
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wrapStyle = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut down `setData`
|
||||||
|
if (wrapStyle === this.data.wrapStyle) return;
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
wrapStyle
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// adjust tab position
|
||||||
|
onScroll(scrollTop) {
|
||||||
|
if (!this.data.sticky) return;
|
||||||
|
|
||||||
|
const { offsetTop } = this.data;
|
||||||
|
|
||||||
|
this.getRect('.van-tabs').then(rect => {
|
||||||
|
const { top, height } = rect;
|
||||||
|
|
||||||
|
this.getRect('.van-tabs__wrap').then(rect => {
|
||||||
|
const { height: wrapHeight } = rect;
|
||||||
|
let position = '';
|
||||||
|
|
||||||
|
if (offsetTop > top + height - wrapHeight) {
|
||||||
|
position = 'bottom';
|
||||||
|
} else if (offsetTop > top) {
|
||||||
|
position = 'top';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('scroll', {
|
||||||
|
scrollTop: scrollTop + offsetTop,
|
||||||
|
isFixed: position === 'top'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (position !== this.data.position) {
|
||||||
|
this.setData({
|
||||||
|
position
|
||||||
|
}, () => {
|
||||||
|
this.setWrapStyle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<view class="custom-class van-tabs van-tabs--{{ type }}">
|
<view class="custom-class van-tabs van-tabs--{{ type }}">
|
||||||
<view style="z-index: {{ zIndex }}" class="van-tabs__wrap {{ scrollable ? 'van-tabs__wrap--scrollable' : '' }} {{ type === 'line' && border ? 'van-hairline--top-bottom' : '' }}">
|
<view style="z-index: {{ zIndex }}; {{ wrapStyle }}" class="van-tabs__wrap {{ scrollable ? 'van-tabs__wrap--scrollable' : '' }} {{ type === 'line' && border ? 'van-hairline--top-bottom' : '' }}">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
scroll-x="{{ scrollable }}"
|
scroll-x="{{ scrollable }}"
|
||||||
scroll-with-animation
|
scroll-with-animation
|
||||||
@ -17,12 +17,20 @@
|
|||||||
style="{{ color && index !== active && type === 'card' && !item.disabled ? 'color: ' + color : '' }} {{ color && index === active && type === 'card' ? ';background-color:' + color : '' }} {{ color ? ';border-color: ' + color : '' }} {{ scrollable ? ';flex-basis:' + (88 / swipeThreshold) + '%' : '' }}"
|
style="{{ color && index !== active && type === 'card' && !item.disabled ? 'color: ' + color : '' }} {{ color && index === active && type === 'card' ? ';background-color:' + color : '' }} {{ color ? ';border-color: ' + color : '' }} {{ scrollable ? ';flex-basis:' + (88 / swipeThreshold) + '%' : '' }}"
|
||||||
bind:tap="onTap"
|
bind:tap="onTap"
|
||||||
>
|
>
|
||||||
{{ item.title }}
|
<view class="van-ellipsis van-tab__title">
|
||||||
|
{{ item.title }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
<view class="van-tabs__content">
|
<view
|
||||||
|
class="van-tabs__content"
|
||||||
|
bind:touchstart="onTouchStart"
|
||||||
|
bind:touchmove="onTouchMove"
|
||||||
|
bind:touchend="onTouchEnd"
|
||||||
|
bind:touchcancel="onTouchEnd"
|
||||||
|
>
|
||||||
<view class="van-tabs__track" style="{{ trackStyle }}">
|
<view class="van-tabs__track" style="{{ trackStyle }}">
|
||||||
<slot />
|
<slot />
|
||||||
</view>
|
</view>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user