mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-08-07 04:59:46 +08:00
[Improvement] Tab animation fluency && position (#379)
This commit is contained in:
parent
2327e75516
commit
5a17bc520a
@ -20,12 +20,12 @@
|
||||
|
||||
- 45+ Reusable components
|
||||
- 90%+ Unit test coverage
|
||||
- Extensive documentation and demos.
|
||||
- Extensive documentation and demos
|
||||
- Support [babel-plugin-import](https://github.com/ant-design/babel-plugin-import)
|
||||
- Support TypeScript
|
||||
|
||||
<p align="center">
|
||||
<img alt="feature demo" src="https://img.yzcdn.cn/public_files/2017/12/05/6a69f80c7ba6754795a7cc6d0766950f.png">
|
||||
<img alt="components preview" src="https://img.yzcdn.cn/public_files/2017/12/05/95f5ee7524b7845abb2f51803a01d65e.png">
|
||||
</p >
|
||||
|
||||
## Install
|
||||
|
@ -22,7 +22,7 @@
|
||||
- 支持 TypeScript
|
||||
|
||||
<p align="center">
|
||||
<img alt="feature demo" src="https://img.yzcdn.cn/public_files/2017/12/05/6a69f80c7ba6754795a7cc6d0766950f.png">
|
||||
<img alt="components preview" src="https://img.yzcdn.cn/public_files/2017/12/05/95f5ee7524b7845abb2f51803a01d65e.png">
|
||||
</p >
|
||||
|
||||
## 安装
|
||||
|
@ -2,7 +2,7 @@
|
||||
<demo-section>
|
||||
<demo-block :title="$t('basicUsage')">
|
||||
<van-tabs :active="active">
|
||||
<van-tab :title="$t('tab') + index" v-for="index in 4" :key="index">
|
||||
<van-tab :title="$t('tab') + index" v-for="index in tabs" :key="index">
|
||||
{{ $t('content') }} {{ index }}
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
@ -64,7 +64,8 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
active: 2
|
||||
active: 2,
|
||||
tabs: [1, 2, 3, 4]
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -106,7 +106,7 @@ export default {
|
||||
|-----------|-----------|-----------|-------------|-------------|
|
||||
| type | There are two style tabs, set this attribute to change tab style | `String` | `line` | `card` |
|
||||
| active | Index of active tab | `String` `Number` | `0` | - |
|
||||
| duration | Toggle tab's animation time | `Number` | `0.3` | - | - |
|
||||
| duration | Toggle tab's animation time | `Number` | `0.2` | - | - |
|
||||
| swipeThreshold | Set swipe tabs threshold | `Number` | `4` | - | - |
|
||||
|
||||
### Tab API
|
||||
|
@ -106,7 +106,7 @@ export default {
|
||||
|-----------|-----------|-----------|-------------|-------------|
|
||||
| type | Tab 样式类型 | `String` | `line` | `card` |
|
||||
| active | 默认激活的 tab | `String` `Number` | `0` | - |
|
||||
| duration | 切换 tab 的动画时间 | `Number` | `0.3` | - | - |
|
||||
| duration | 切换 tab 的动画时间 | `Number` | `0.2` | - | - |
|
||||
| swipeThreshold | 滚动阀值,设置 Tab 超过多少个可滚动 | `Number` | `4` | - | - |
|
||||
|
||||
### Tab API
|
||||
|
@ -72,7 +72,6 @@
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import { isServer } from '../../utils';
|
||||
import Popup from '../../popup';
|
||||
import Toast from '../../toast';
|
||||
import SkuHeader from '../components/SkuHeader';
|
||||
@ -177,7 +176,7 @@ export default {
|
||||
|
||||
computed: {
|
||||
bodyStyle() {
|
||||
if (isServer) {
|
||||
if (this.$isServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="van-tab__pane" :class="{ 'van-tab__pane--select': key === $parent.curActive }">
|
||||
<div class="van-tab__pane" :class="{ 'van-tab__pane--select': index === parentGroup.curActive }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@ -20,46 +20,19 @@ export default {
|
||||
disabled: Boolean
|
||||
},
|
||||
|
||||
data() {
|
||||
computed: {
|
||||
index() {
|
||||
return this.parentGroup.tabs.indexOf(this);
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.findParentByName('van-tabs');
|
||||
const nextIndex = this.parentGroup.tabs.length;
|
||||
this.updateParentData(nextIndex);
|
||||
return {
|
||||
key: nextIndex
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
title() {
|
||||
this.updateParentData();
|
||||
},
|
||||
disabled() {
|
||||
this.updateParentData();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateParentData(nextIndex) {
|
||||
const index = arguments.length ? nextIndex : this.key;
|
||||
this.parentGroup.tabs.splice(index, 1, {
|
||||
title: this.title,
|
||||
disabled: this.disabled,
|
||||
index
|
||||
});
|
||||
}
|
||||
this.parentGroup.tabs.push(this);
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
const key = this.key;
|
||||
const tabs = this.parentGroup.tabs;
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
/* istanbul ignore else */
|
||||
if (tabs[i].index === key) {
|
||||
this.parentGroup.tabs.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.parentGroup.tabs.splice(this.index, 1);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,39 +1,23 @@
|
||||
<template>
|
||||
<div class="van-tabs" :class="`van-tabs--${type}`">
|
||||
<div class="van-tabs__nav-wrap" v-if="type === 'line' && tabs.length > swipeThreshold">
|
||||
<div class="van-tabs__swipe" ref="swipe">
|
||||
<div class="van-tabs__nav van-tabs__nav--line">
|
||||
<div class="van-tabs__nav-bar" :style="navBarStyle"></div>
|
||||
<div
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
class="van-tab van-hairline"
|
||||
:class="{ 'van-tab--active': index === curActive }"
|
||||
ref="tabkey"
|
||||
@click="handleTabClick(index)"
|
||||
>
|
||||
<span>{{ tab.title }}</span>
|
||||
</div>
|
||||
<div :class="{ 'van-tabs__swipe': 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
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
ref="tabs"
|
||||
class="van-tab"
|
||||
:class="{
|
||||
'van-tab--active': index === curActive,
|
||||
'van-tab--disabled': tab.disabled
|
||||
}"
|
||||
@click="onClick(index)"
|
||||
>
|
||||
<span>{{ tab.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="van-tabs__nav"
|
||||
:class="`van-tabs__nav--${type}`"
|
||||
>
|
||||
<div class="van-tabs__nav-bar" :style="navBarStyle" v-if="type === 'line'"></div>
|
||||
<div
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
class="van-tab van-hairline"
|
||||
:class="{ 'van-tab--active': index === curActive }"
|
||||
ref="tabkey"
|
||||
@click="handleTabClick(index)"
|
||||
>
|
||||
<span>{{ tab.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="van-tabs__content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@ -41,192 +25,126 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import swipe from './swipe';
|
||||
import translateUtil from '../utils/transition';
|
||||
import { raf } from '../utils/raf';
|
||||
|
||||
export default {
|
||||
name: 'van-tabs',
|
||||
export default {
|
||||
name: 'van-tabs',
|
||||
|
||||
props: {
|
||||
// 外部传入的激活的tab标签
|
||||
active: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 是默认的line还是card
|
||||
type: {
|
||||
type: String,
|
||||
default: 'line'
|
||||
},
|
||||
// 切换tab的动画时间
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 0.3
|
||||
},
|
||||
swipeThreshold: {
|
||||
type: Number,
|
||||
default: 4
|
||||
}
|
||||
props: {
|
||||
active: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'line'
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 0.2
|
||||
},
|
||||
swipeThreshold: {
|
||||
type: Number,
|
||||
default: 4
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
this.winWidth = this.$isServer ? 0 : window.innerWidth;
|
||||
|
||||
return {
|
||||
tabs: [],
|
||||
curActive: 0,
|
||||
navBarStyle: {}
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
active(val) {
|
||||
this.correctActive(val);
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
tabs: [],
|
||||
curActive: +this.active,
|
||||
isSwiping: false,
|
||||
isInitEvents: false,
|
||||
navBarStyle: {}
|
||||
};
|
||||
tabs(tabs) {
|
||||
this.correctActive(this.curActive);
|
||||
this.setNavBar();
|
||||
},
|
||||
|
||||
watch: {
|
||||
active(val) {
|
||||
this.curActive = +val;
|
||||
},
|
||||
curActive() {
|
||||
this.scrollIntoView();
|
||||
this.setNavBar();
|
||||
}
|
||||
},
|
||||
|
||||
curActive() {
|
||||
this.setNavBarStyle();
|
||||
/* istanbul ignore else */
|
||||
if (this.tabs.length > this.swipeThreshold) {
|
||||
this.doOnValueChange();
|
||||
}
|
||||
},
|
||||
|
||||
tabs(val) {
|
||||
this.$nextTick(() => {
|
||||
this.setNavBarStyle();
|
||||
if (val.length > this.swipeThreshold) {
|
||||
this.initEvents();
|
||||
this.doOnValueChange();
|
||||
} else {
|
||||
this.isInitEvents = false;
|
||||
}
|
||||
mounted() {
|
||||
this.correctActive(this.active);
|
||||
this.setNavBar();
|
||||
},
|
||||
|
||||
const activeExist = val.some(tab => tab.index === this.curActive);
|
||||
if (!activeExist) {
|
||||
this.curActive = val[0].index || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scrollable() {
|
||||
return this.tabs.length > this.swipeThreshold;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
swipeWidth() {
|
||||
return this.$refs.swipe && this.$refs.swipe.getBoundingClientRect().width;
|
||||
},
|
||||
maxTranslate() {
|
||||
/* istanbul ignore if */
|
||||
if (!this.$refs.tabkey) return;
|
||||
|
||||
const lastTab = this.$refs.tabkey[this.tabs.length - 1];
|
||||
const lastTabWidth = lastTab.offsetWidth;
|
||||
const lastTabOffsetLeft = lastTab.offsetLeft;
|
||||
|
||||
return (lastTabOffsetLeft + lastTabWidth) - this.swipeWidth;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 页面载入完成
|
||||
methods: {
|
||||
setNavBar() {
|
||||
this.$nextTick(() => {
|
||||
this.setNavBarStyle();
|
||||
|
||||
if (this.tabs.length > this.swipeThreshold) {
|
||||
this.initEvents();
|
||||
this.doOnValueChange();
|
||||
const tab = this.$refs.tabs[this.curActive];
|
||||
this.navBarStyle = {
|
||||
width: `${tab.offsetWidth || 0}px`,
|
||||
transform: `translate3d(${tab.offsetLeft || 0}px, 0, 0)`,
|
||||
transitionDuration: `${this.duration}s`
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* `type`为`line`时,tab下方的横线的样式
|
||||
*/
|
||||
setNavBarStyle() {
|
||||
if (this.type !== 'line' || !this.$refs.tabkey) return {};
|
||||
|
||||
const tabKey = this.curActive;
|
||||
const elem = this.$refs.tabkey[tabKey];
|
||||
const offsetWidth = `${elem.offsetWidth || 0}px`;
|
||||
const offsetLeft = `${elem.offsetLeft || 0}px`;
|
||||
|
||||
this.navBarStyle = {
|
||||
width: offsetWidth,
|
||||
transform: `translate3d(${offsetLeft}, 0, 0)`,
|
||||
transitionDuration: `${this.duration}s`
|
||||
};
|
||||
},
|
||||
|
||||
handleTabClick(index) {
|
||||
if (this.tabs[index].disabled) {
|
||||
this.$emit('disabled', index);
|
||||
return;
|
||||
}
|
||||
correctActive(active) {
|
||||
active = +active;
|
||||
const exist = this.tabs.some(tab => tab.index === active);
|
||||
this.curActive = exist ? active : (this.tabs[0].index || 0);
|
||||
},
|
||||
|
||||
onClick(index) {
|
||||
if (this.tabs[index].disabled) {
|
||||
this.$emit('disabled', index);
|
||||
} else {
|
||||
this.$emit('click', index);
|
||||
this.curActive = index;
|
||||
},
|
||||
|
||||
/**
|
||||
* 将当前value值转换为需要translate的值
|
||||
*/
|
||||
value2Translate(value) {
|
||||
/* istanbul ignore if */
|
||||
if (!this.$refs.tabkey) return 0;
|
||||
|
||||
const tab = this.$refs.tabkey[value];
|
||||
const maxTranslate = this.maxTranslate;
|
||||
const tabWidth = tab.offsetWidth;
|
||||
const tabOffsetLeft = tab.offsetLeft;
|
||||
let translate = tabOffsetLeft + (tabWidth * 2.7) - this.swipeWidth;
|
||||
if (translate < 0) {
|
||||
translate = 0;
|
||||
}
|
||||
|
||||
return -1 * (translate > maxTranslate ? maxTranslate : translate);
|
||||
},
|
||||
|
||||
initEvents() {
|
||||
const el = this.$refs.swipe;
|
||||
if (!el || this.isInitEvents) return;
|
||||
|
||||
this.isInitEvents = true;
|
||||
let swipeState = {};
|
||||
|
||||
swipe(el, {
|
||||
start: event => {
|
||||
swipeState = {
|
||||
start: new Date(),
|
||||
startLeft: event.pageX,
|
||||
startTranslateLeft: translateUtil.getElementTranslate(el).left
|
||||
};
|
||||
},
|
||||
|
||||
drag: event => {
|
||||
this.isSwiping = true;
|
||||
|
||||
swipeState.left = event.pageX;
|
||||
const deltaX = swipeState.left - swipeState.startLeft;
|
||||
const translate = swipeState.startTranslateLeft + deltaX;
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (translate > 0 || (translate * -1) > this.maxTranslate) return;
|
||||
|
||||
translateUtil.translateElement(el, translate, null);
|
||||
},
|
||||
|
||||
end: () => {
|
||||
this.isSwiping = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
doOnValueChange() {
|
||||
const value = +this.curActive;
|
||||
const swipe = this.$refs.swipe;
|
||||
|
||||
translateUtil.translateElement(swipe, this.value2Translate(value), null);
|
||||
}
|
||||
},
|
||||
|
||||
scrollIntoView() {
|
||||
if (!this.scrollable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tab = this.$refs.tabs[this.curActive];
|
||||
const { nav } = this.$refs;
|
||||
const { winWidth } = this;
|
||||
const { scrollLeft } = nav;
|
||||
const { offsetLeft, offsetWidth: tabWidth } = tab;
|
||||
|
||||
// out of right side
|
||||
if ((winWidth + scrollLeft - offsetLeft - tabWidth * 1.8) < 0) {
|
||||
this.scrollTo(nav, scrollLeft, offsetLeft + tabWidth * 1.8 - winWidth);
|
||||
}
|
||||
// out of left side
|
||||
else if (offsetLeft < (scrollLeft + tabWidth * 0.8)) {
|
||||
this.scrollTo(nav, scrollLeft, offsetLeft - tabWidth * 0.8);
|
||||
}
|
||||
},
|
||||
|
||||
scrollTo(el, from, to) {
|
||||
let count = 0;
|
||||
const frames = Math.round(this.duration * 1000 / 16);
|
||||
const animate = () => {
|
||||
el.scrollLeft += (to - from) / frames;
|
||||
if (++count < frames) {
|
||||
raf(animate);
|
||||
}
|
||||
}
|
||||
animate();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,46 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
let isSwiping = false;
|
||||
|
||||
const supportTouch = !Vue.prototype.$isServer && 'ontouchstart' in window;
|
||||
|
||||
export default function(element, options) {
|
||||
const moveFn = function(event) {
|
||||
if (options.drag) {
|
||||
options.drag(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
};
|
||||
|
||||
const endFn = function(event) {
|
||||
if (!supportTouch) {
|
||||
document.removeEventListener('mousemove', moveFn);
|
||||
document.removeEventListener('mouseup', endFn);
|
||||
}
|
||||
|
||||
isSwiping = false;
|
||||
|
||||
if (options.end) {
|
||||
options.end(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener(supportTouch ? 'touchstart' : 'mousedown', function(event) {
|
||||
if (isSwiping) return;
|
||||
|
||||
if (!supportTouch) {
|
||||
document.addEventListener('mousemove', moveFn);
|
||||
document.addEventListener('mouseup', endFn);
|
||||
}
|
||||
isSwiping = true;
|
||||
|
||||
if (options.start) {
|
||||
options.start(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
});
|
||||
|
||||
if (supportTouch) {
|
||||
element.addEventListener('touchmove', moveFn);
|
||||
element.addEventListener('touchend', endFn);
|
||||
element.addEventListener('touchcancel', endFn);
|
||||
}
|
||||
};
|
32
packages/utils/raf.js
Normal file
32
packages/utils/raf.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* requestAnimationFrame polyfill
|
||||
*/
|
||||
|
||||
import { isServer } from './index';
|
||||
|
||||
let prev = Date.now();
|
||||
function fallback(fn) {
|
||||
const curr = Date.now();
|
||||
const ms = Math.max(0, 16 - (curr - prev));
|
||||
const id = setTimeout(fn, ms);
|
||||
prev = curr + ms;
|
||||
return id;
|
||||
}
|
||||
|
||||
const global = isServer ? global : window;
|
||||
const iRaf =
|
||||
global.requestAnimationFrame ||
|
||||
global.webkitRequestAnimationFrame ||
|
||||
fallback;
|
||||
const iCancel =
|
||||
global.cancelAnimationFrame ||
|
||||
global.webkitCancelAnimationFrame ||
|
||||
global.clearTimeout;
|
||||
|
||||
export function raf(fn) {
|
||||
return iRaf.call(global, fn);
|
||||
}
|
||||
|
||||
export function cancel(id) {
|
||||
iCancel.call(global, id);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
var exportObj = {};
|
||||
|
||||
if (!Vue.prototype.$isServer) {
|
||||
var cssPrefix = '-webkit-';
|
||||
var vendorPrefix = 'Webkit';
|
||||
var transformProperty = vendorPrefix + 'Transform';
|
||||
var transformStyleName = cssPrefix + 'transform';
|
||||
var transitionProperty = vendorPrefix + 'Transition';
|
||||
var transitionStyleName = cssPrefix + 'transition';
|
||||
var transitionEndProperty = vendorPrefix.toLowerCase() + 'TransitionEnd';
|
||||
|
||||
var getTranslate = function(element) {
|
||||
var result = { left: 0, top: 0 };
|
||||
if (element === null || element.style === null) return result;
|
||||
|
||||
var transform = element.style[transformProperty];
|
||||
var matches = /translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*translateZ\(0px\)/ig.exec(transform);
|
||||
if (matches) {
|
||||
result.left = +matches[1];
|
||||
result.top = +matches[3];
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var translateElement = function(element, x, y) {
|
||||
if (x === null && y === null) return;
|
||||
|
||||
if (element === null || element === undefined || element.style === null) return;
|
||||
|
||||
if (!element.style[transformProperty] && x === 0 && y === 0) return;
|
||||
|
||||
if (x === null || y === null) {
|
||||
var translate = getTranslate(element);
|
||||
if (x === null) {
|
||||
x = translate.left;
|
||||
}
|
||||
if (y === null) {
|
||||
y = translate.top;
|
||||
}
|
||||
}
|
||||
|
||||
cancelTranslateElement(element);
|
||||
element.style[transformProperty] += ' translate(' + (x ? (x + 'px') : '0px') + ',' + (y ? (y + 'px') : '0px') + ') translateZ(0px)';
|
||||
};
|
||||
|
||||
var cancelTranslateElement = function(element) {
|
||||
if (element === null || element.style === null) return;
|
||||
|
||||
var transformValue = element.style[transformProperty];
|
||||
|
||||
if (transformValue) {
|
||||
transformValue = transformValue.replace(/translate\(\s*(-?\d+(\.?\d+?)?)px,\s*(-?\d+(\.\d+)?)px\)\s*translateZ\(0px\)/g, '');
|
||||
element.style[transformProperty] = transformValue;
|
||||
}
|
||||
};
|
||||
|
||||
exportObj = {
|
||||
transformProperty: transformProperty,
|
||||
transformStyleName: transformStyleName,
|
||||
transitionProperty: transitionProperty,
|
||||
transitionStyleName: transitionStyleName,
|
||||
transitionEndProperty: transitionEndProperty,
|
||||
getElementTranslate: getTranslate,
|
||||
translateElement: translateElement,
|
||||
cancelTranslateElement: cancelTranslateElement
|
||||
};
|
||||
};
|
||||
|
||||
export default exportObj;
|
||||
|
@ -4,46 +4,38 @@
|
||||
.van-tabs {
|
||||
position: relative;
|
||||
|
||||
&__nav-wrap {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__swipe {
|
||||
user-select: none;
|
||||
transition: transform linear .2s;
|
||||
|
||||
.van-tab {
|
||||
flex: 0 0 22%;
|
||||
}
|
||||
|
||||
.van-tabs__nav {
|
||||
overflow: visible;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__nav {
|
||||
overflow: hidden;
|
||||
transition: transform .5s cubic-bezier(.645, .045, .355, 1);
|
||||
position: relative;
|
||||
display: flex;
|
||||
position: relative;
|
||||
background-color: $white;
|
||||
|
||||
&--line {
|
||||
height: 44px;
|
||||
|
||||
.van-tab {
|
||||
&::after {
|
||||
border-width: 1px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--card {
|
||||
height: 28px;
|
||||
margin: 0 15px;
|
||||
background-color: $white;
|
||||
border-radius: 2px;
|
||||
border: 1px solid $gray-darker;
|
||||
overflow: hidden;
|
||||
|
||||
.van-tab {
|
||||
color: $gray-darker;
|
||||
@ -55,8 +47,8 @@
|
||||
}
|
||||
|
||||
&.van-tab--active {
|
||||
background-color: $gray-darker;
|
||||
color: $white;
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,27 +56,25 @@
|
||||
|
||||
&__nav-bar {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
background-color: #f13e3a;
|
||||
transition: transform .3s cubic-bezier(.645, .045, .355, 1);
|
||||
transform-origin: 0 0;
|
||||
position: absolute;
|
||||
background-color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.van-tab {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
padding: 0 5px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
color: $text-color;
|
||||
background-color: $white;
|
||||
font-size: 14px;
|
||||
line-height: 44px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
background-color: $white;
|
||||
min-width: 0; /* hack for flex ellipsis */
|
||||
|
||||
span {
|
||||
@ -92,10 +82,22 @@
|
||||
@mixin ellipsis;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $active-color;
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: $red;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
color: $gray;
|
||||
|
||||
&:active {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&__pane {
|
||||
display: none;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user