diff --git a/example/pages/tab/index.js b/example/pages/tab/index.js
index 9906a9b9..18cd39e3 100644
--- a/example/pages/tab/index.js
+++ b/example/pages/tab/index.js
@@ -3,7 +3,8 @@ import Page from '../../common/page';
Page({
data: {
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) {
@@ -25,5 +26,11 @@ Page({
title: `点击标签 ${event.detail.index + 1}`,
icon: 'none'
});
+ },
+
+ onPageScroll(event) {
+ this.setData({
+ scrollTop: event.scrollTop
+ });
}
});
diff --git a/example/pages/tab/index.wxml b/example/pages/tab/index.wxml
index 300c71f8..3ac4f918 100644
--- a/example/pages/tab/index.wxml
+++ b/example/pages/tab/index.wxml
@@ -69,6 +69,20 @@
+
+
+
+
+ {{ '内容' + item }}
+
+
+
+
+
+
+
+
+
+
+ {{ '内容' + item }}
+
+
+
+
diff --git a/packages/tab/README.md b/packages/tab/README.md
index a1cbff72..d38caeb2 100644
--- a/packages/tab/README.md
+++ b/packages/tab/README.md
@@ -111,6 +111,19 @@ Page({
});
```
+#### 粘性布局
+
+通过`sticky`属性可以开启粘性布局,粘性布局下,当 Tab 滚动到顶部时会自动吸顶
+
+```html
+
+ 内容 1
+ 内容 2
+ 内容 3
+ 内容 4
+
+```
+
#### 切换动画
可以通过`animated`来设置是否启用切换tab时的动画。
@@ -124,6 +137,19 @@ Page({
```
+#### 滑动切换
+
+通过`swipeable`属性可以开启滑动切换标签页
+
+```html
+
+ 内容 1
+ 内容 2
+ 内容 3
+ 内容 4
+
+```
+
### Tabs API
| 参数 | 说明 | 类型 | 默认值 |
@@ -137,6 +163,10 @@ Page({
| line-width | 底部条宽度 (px) | `Number` | 与当前标签等宽 |
| swipe-threshold | 滚动阈值,设置标签数量超过多少个可滚动 | `Number` | `4` |
| animated | 是否使用动画切换 Tabs | `Boolean` | `false` |
+| swipeable | 是否开启手势滑动切换 | `Boolean` | `false` |
+| sticky | 是否使用粘性定位布局 | `Boolean` | `false` |
+| offset-top | 粘性定位布局下与顶部的最小距离,单位 px | `Number` | `0` |
+| scroll-top | 页面的`scrollTop`,粘性布局下必须要传入,单位 px | `Number` | `0` |
### Tab API
@@ -158,6 +188,7 @@ Page({
| bind:click | 点击标签时触发 | index:标签索引,title:标题 |
| bind:change | 当前激活的标签改变时触发 | index:标签索引,title:标题 |
| bind:disabled | 点击被禁用的标签时触发 | index:标签索引,title:标题 |
+| bind:scroll | 滚动时触发 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } |
### 外部样式类
diff --git a/packages/tabs/index.ts b/packages/tabs/index.ts
index 60c248ca..476aa251 100644
--- a/packages/tabs/index.ts
+++ b/packages/tabs/index.ts
@@ -1,4 +1,5 @@
import { VantComponent } from '../common/component';
+import { touch } from '../mixins/touch';
type TabItemData = {
active: boolean;
@@ -8,6 +9,8 @@ type TabItemData = {
};
VantComponent({
+ mixins: [touch],
+
relation: {
name: 'tab',
type: 'descendant',
@@ -54,7 +57,17 @@ VantComponent({
type: Number,
value: 4
},
- animated: Boolean
+ animated: Boolean,
+ sticky: Boolean,
+ offsetTop: {
+ type: Number,
+ value: 0
+ },
+ swipeable: Boolean,
+ scrollTop: {
+ type: Number,
+ value: 0
+ }
},
data: {
@@ -62,7 +75,9 @@ VantComponent({
lineStyle: '',
scrollLeft: 0,
scrollable: false,
- trackStyle: ''
+ trackStyle: '',
+ wrapStyle: '',
+ position: ''
},
watch: {
@@ -74,7 +89,9 @@ VantComponent({
color: 'setLine',
lineWidth: 'setLine',
active: 'setActiveTab',
- animated: 'setTrack'
+ animated: 'setTrack',
+ scrollTop: 'onScroll',
+ offsetTop: 'setWrapStyle'
},
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();
+ });
+ }
+ });
+ });
}
}
});
diff --git a/packages/tabs/index.wxml b/packages/tabs/index.wxml
index 6f9a5a36..f377e4bf 100644
--- a/packages/tabs/index.wxml
+++ b/packages/tabs/index.wxml
@@ -1,5 +1,5 @@
-
+
- {{ item.title }}
+
+ {{ item.title }}
+
-
+