diff --git a/docs/demos/views/tab.vue b/docs/demos/views/tab.vue index f44242dde..743e892bc 100644 --- a/docs/demos/views/tab.vue +++ b/docs/demos/views/tab.vue @@ -58,6 +58,14 @@ + + + + + {{ $t('content') }} {{ index }} + + + @@ -71,7 +79,8 @@ export default { title4: '样式风格', title5: '点击事件', title6: '粘性布局', - title7: '自定义标签' + title7: '自定义标签', + title8: '滑动切换' }, 'en-US': { tab: 'Tab ', @@ -81,7 +90,8 @@ export default { title4: 'Card Style', title5: 'Click Event', title6: 'Sticky', - title7: 'Custom Tab' + title7: 'Custom Tab', + title8: 'Swipeable' } }, diff --git a/docs/markdown/en-US/tab.md b/docs/markdown/en-US/tab.md index f7d2a4e21..8cdb54a83 100644 --- a/docs/markdown/en-US/tab.md +++ b/docs/markdown/en-US/tab.md @@ -124,6 +124,18 @@ Use title slot to custom tab title ``` +#### Swipeable + +In swipeable mode, you can switch tabs with swipe gestrue in the content + +```html + + + content {{ index }} + + +``` + ### Tabs API | Attribute | Description | Type | Default | Accepted Values | @@ -132,6 +144,8 @@ Use title slot to custom tab title | active | Index of active tab | `String` `Number` | `0` | - | | duration | Toggle tab's animation time | `Number` | `0.2` | - | - | | swipe-threshold | Set swipe tabs threshold | `Number` | `4` | - | - | +| sticky | Whether to use sticky mode | `Boolean` | `false` | - | +| swipeable | Whether to switch tabs with swipe gestrue in the content | `Boolean` | `false` | - | ### Tab API diff --git a/docs/markdown/zh-CN/tab.md b/docs/markdown/zh-CN/tab.md index 3ac38def3..c234922cc 100644 --- a/docs/markdown/zh-CN/tab.md +++ b/docs/markdown/zh-CN/tab.md @@ -124,6 +124,18 @@ export default { ``` +#### 滑动切换 + +通过`swipeable`属性可以开启滑动切换tab + +```html + + + 内容 {{ index }} + + +``` + ### Tabs API | 参数 | 说明 | 类型 | 默认值 | 可选 | @@ -133,6 +145,7 @@ export default { | duration | 切换 tab 的动画时间 | `Number` | `0.2` | - | | swipe-threshold | 滚动阀值,设置 Tab 超过多少个可滚动 | `Number` | `4` | - | | sticky | 是否使用粘性定位布局 | `Boolean` | `false` | - | +| swipeable | 是否可以滑动内容切换 | `Boolean` | `false` | - | ### Tab API diff --git a/packages/stepper/index.vue b/packages/stepper/index.vue index 7b925c0e3..4ab313c3c 100644 --- a/packages/stepper/index.vue +++ b/packages/stepper/index.vue @@ -103,6 +103,7 @@ export default create({ onInput(event) { const { value } = event.target; this.currentValue = value ? this.correctValue(+value) : value; + event.target.value = this.currentValue; this.emitInput(); }, diff --git a/packages/tabs/index.vue b/packages/tabs/index.vue index e55607139..f5bd1c9aa 100644 --- a/packages/tabs/index.vue +++ b/packages/tabs/index.vue @@ -26,7 +26,7 @@ -
+
@@ -63,7 +63,8 @@ export default create({ swipeThreshold: { type: Number, default: 4 - } + }, + swipeable: Boolean }, data() { @@ -71,7 +72,11 @@ export default create({ tabs: [], position: 'content-top', curActive: 0, - navBarStyle: {} + navBarStyle: {}, + pos: { + x: 0, + y: 0 + } }; }, @@ -115,6 +120,9 @@ export default create({ if (this.sticky) { this.scrollHandler(true); } + if (this.swipeable) { + this.swipeableHandler(true); + } this.scrollIntoView(); }); }, @@ -124,6 +132,10 @@ export default create({ if (this.sticky) { this.scrollHandler(false); } + /* istanbul ignore next */ + if (this.swipeable) { + this.swipeableHandler(false); + } }, methods: { @@ -136,6 +148,46 @@ export default create({ } }, + // whether to bind content swipe listener + swipeableHandler(init) { + const swipeableEl = this.$refs.content; + + this.touchMoveHandler = scrollUtils.debounce(this.watchTouchMove.bind(this), 500); + + (init ? on : off)(swipeableEl, 'touchstart', this.recordTouchStartPosition, true); + (init ? on : off)(swipeableEl, 'touchmove', this.touchMoveHandler, true); + }, + + // record swipe touch start position + recordTouchStartPosition(e) { + this.pos = { + x: e.touches[0].clientX, + y: e.touches[0].clientY + }; + }, + + // watch swipe touch move + watchTouchMove(e) { + const { pos } = this; + const dx = e.touches[0].clientX - pos.x; + const dy = e.touches[0].clientY - pos.y; + const isForward = dx > 0; + const isHorizontal = Math.abs(dy) < Math.abs(dx); + const minSwipeDistance = 50; + + /* istanbul ignore else */ + if (isHorizontal && Math.abs(dx) >= minSwipeDistance) { + const active = +this.curActive; + + /* istanbul ignore else */ + if (isForward && active !== 0) { + this.curActive = active - 1; + } else if (!isForward && active !== this.tabs.length - 1) { + this.curActive = active + 1; + } + } + }, + // adjust tab position onScroll() { const scrollTop = scrollUtils.getScrollTop(this.scrollEl); diff --git a/packages/vant-css/src/vant-icon-743c0e.ttf b/packages/vant-css/src/vant-icon-743c0e.ttf new file mode 100644 index 000000000..efcd83914 Binary files /dev/null and b/packages/vant-css/src/vant-icon-743c0e.ttf differ diff --git a/test/components/tabs.vue b/test/components/tabs.vue index bb0773a75..71486b498 100644 --- a/test/components/tabs.vue +++ b/test/components/tabs.vue @@ -1,5 +1,12 @@