[new feature] Tab support sticky (#382)

This commit is contained in:
neverland
2017-12-06 14:19:08 +08:00
committed by GitHub
parent beb3ed141d
commit 80c2fdc18f
7 changed files with 148 additions and 33 deletions
+49 -5
View File
@@ -1,6 +1,6 @@
<template>
<div class="van-tabs" :class="`van-tabs--${type}`">
<div :class="{ 'van-tabs__swipe': scrollable, 'van-hairline--top-bottom': type === 'line' }">
<div class="van-tabs" :class="[`van-tabs--${type}`, { 'van-tabs--fixed': fixed }]">
<div class="van-tabs__wrap" :class="{ 'van-tabs--scrollbale': scrollable, 'van-hairline--top-bottom': type === 'line' }">
<div class="van-tabs__nav" :class="`van-tabs__nav--${type}`" ref="nav">
<div v-if="type === 'line'" class="van-tabs__nav-bar" :style="navBarStyle" />
<div
@@ -26,6 +26,7 @@
<script>
import { raf } from '../utils/raf';
import scrollUtils from '../utils/scroll';
export default {
name: 'van-tabs',
@@ -46,14 +47,17 @@ export default {
swipeThreshold: {
type: Number,
default: 4
}
},
sticky: Boolean
},
data() {
/* istanbul ignore next */
this.winWidth = this.$isServer ? 0 : window.innerWidth;
return {
tabs: [],
fixed: false,
curActive: 0,
navBarStyle: {}
};
@@ -72,23 +76,56 @@ export default {
curActive() {
this.scrollIntoView();
this.setNavBar();
},
sticky(isSticky) {
this.scrollHandler(isSticky);
}
},
mounted() {
this.correctActive(this.active);
this.setNavBar();
if (this.sticky) {
this.scrollHandler(true);
}
},
destoryed() {
/* istanbul ignore next */
if (this.sticky) {
this.scrollHandler(false);
}
},
computed: {
// whether the nav is scrollable
scrollable() {
return this.tabs.length > this.swipeThreshold;
}
},
methods: {
// whether to bind sticky listener
scrollHandler(init) {
this.scrollEl = this.scrollEl || scrollUtils.getScrollEventTarget(this.$el);
this.scrollEl[init ? 'addEventListener' : 'removeEventListener']('scroll', this.onScroll);
this.onScroll();
},
// fixed tab when scrollTop > distance to top
onScroll() {
this.fixed = scrollUtils.getScrollTop(this.scrollEl) > scrollUtils.getElementTop(this.$el);
},
// update nav bar style
setNavBar() {
this.$nextTick(() => {
if (!this.$refs.tabs) {
return;
}
const tab = this.$refs.tabs[this.curActive];
this.navBarStyle = {
width: `${tab.offsetWidth || 0}px`,
@@ -98,12 +135,15 @@ export default {
});
},
// correct the value of active
correctActive(active) {
active = +active;
const exist = this.tabs.some(tab => tab.index === active);
this.curActive = exist ? active : (this.tabs[0].index || 0);
const defaultActive = (this.tabs[0] || {}).index || 0;
this.curActive = exist ? active : defaultActive;
},
// emit event when clicked
onClick(index) {
if (this.tabs[index].disabled) {
this.$emit('disabled', index);
@@ -113,8 +153,9 @@ export default {
}
},
// scroll active tab into view
scrollIntoView() {
if (!this.scrollable) {
if (!this.scrollable || !this.$refs.tabs) {
return;
}
@@ -125,6 +166,7 @@ export default {
const { offsetLeft, offsetWidth: tabWidth } = tab;
// out of right side
/* istanbul ignore next */
if ((winWidth + scrollLeft - offsetLeft - tabWidth * 1.8) < 0) {
this.scrollTo(nav, scrollLeft, offsetLeft + tabWidth * 1.8 - winWidth);
}
@@ -134,11 +176,13 @@ export default {
}
},
// animate the scrollLeft of nav
scrollTo(el, from, to) {
let count = 0;
const frames = Math.round(this.duration * 1000 / 16);
const animate = () => {
el.scrollLeft += (to - from) / frames;
/* istanbul ignore next */
if (++count < frames) {
raf(animate);
}
+41 -22
View File
@@ -1,45 +1,31 @@
@import './common/var.css';
@import './mixins/ellipsis.css';
$van-tabs-line-height: 44px;
$van-tabs-card-height: 28px;
.van-tabs {
position: relative;
&__swipe {
user-select: none;
.van-tab {
flex: 0 0 22%;
}
.van-tabs__nav {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
background-color: transparent;
}
}
}
&__nav {
display: flex;
position: relative;
user-select: none;
background-color: $white;
&--line {
height: 44px;
height: $van-tabs-line-height;
}
&--card {
height: 28px;
margin: 0 15px;
border-radius: 2px;
height: $van-tabs-card-height;
border: 1px solid $gray-darker;
.van-tab {
color: $gray-darker;
line-height: 28px;
line-height: $van-tabs-card-height;
border-right: 1px solid $gray-darker;
&:last-child {
@@ -62,6 +48,39 @@
position: absolute;
background-color: $red;
}
&--scrollbale {
.van-tab {
flex: 0 0 22%;
}
.van-tabs__nav {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
background-color: transparent;
}
}
}
&--fixed {
.van-tabs__wrap {
top: 0;
left: 0;
right: 0;
position: fixed;
}
&.van-tabs--line {
padding-top: $van-tabs-line-height;
}
&.van-tabs--card {
padding-top: $van-tabs-card-height;
}
}
}
.van-tab {
@@ -71,7 +90,7 @@
font-size: 14px;
position: relative;
color: $text-color;
line-height: 44px;
line-height: $van-tabs-line-height;
text-align: center;
box-sizing: border-box;
background-color: $white;