3 changed files with 252 additions and 0 deletions
@ -0,0 +1,29 @@ |
|||
<template> |
|||
<view |
|||
class='tab-pane-item' |
|||
> |
|||
<slot></slot> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name:'TabPane', |
|||
props:{ |
|||
current:{ |
|||
default:0, |
|||
type:Number |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style lang="stylus"> |
|||
.tab-pane-item |
|||
width: 100%; |
|||
height 100% |
|||
display: inline-block |
|||
white-space: initial; |
|||
vertical-align: top; |
|||
font-size: 24upx; |
|||
box-sizing: border-box; |
|||
overflow: auto |
|||
</style> |
|||
@ -0,0 +1,159 @@ |
|||
|
|||
<template> |
|||
<view |
|||
class="tabs" |
|||
> |
|||
<scroll-view class="active-switch" scroll-x :scroll-into-view="id" scroll-with-animation> |
|||
<view class="switch-container" > |
|||
<view |
|||
v-for="(item,index) in TabList" :key="index" |
|||
:class="['active-item',currentTab==index&&'focus',TabList.length==3&&'fix']" |
|||
@tap="tabChange(index)" |
|||
:id='`tab_${index}`' |
|||
> |
|||
<view class="item">{{item.title}}</view> |
|||
</view> |
|||
<view class="focus-line" |
|||
:class="[TabList.length==3&&'fix']" |
|||
:style="{transform:transformX}" |
|||
> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
<view class="tab-pane-view" |
|||
@touchstart='touchstart' |
|||
@touchend='touchend' |
|||
> |
|||
<view |
|||
class="tab-pane-group" |
|||
:style="{transform:transformXx}" |
|||
> |
|||
<slot></slot> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name:'Tabs', |
|||
data(){ |
|||
return { |
|||
id:'tab_0', |
|||
start:0 |
|||
} |
|||
}, |
|||
props:{ |
|||
TabList:{ |
|||
default:()=>{ |
|||
return [] |
|||
}, |
|||
type:Array |
|||
}, |
|||
currentTab:{ |
|||
default:0, |
|||
type:Number |
|||
} |
|||
}, |
|||
computed:{ |
|||
transformX(){ |
|||
let currentTab = this.currentTab; |
|||
return `translate3d(${currentTab*100}%, 0px, 0px)` |
|||
}, |
|||
transformXx(){ |
|||
let currentTab = this.currentTab; |
|||
return `translate3d(-${currentTab*100}%, 0px, 0px)` |
|||
} |
|||
}, |
|||
methods:{ |
|||
tabChange(index){ |
|||
if(this.currentTab!=index){ |
|||
console.log(`emit:${index}`); |
|||
this.$emit('tabs',index); |
|||
this.bus.$emit('tabs',index) //事件,这里触发tabs事件 |
|||
this.id = `tab_${index}` |
|||
} |
|||
}, |
|||
touchstart(e){ |
|||
this.start = e.touches[0].clientX; |
|||
}, |
|||
touchend(e){ |
|||
let end = e.changedTouches[0].clientX; |
|||
if(end-this.start>100&&this.currentTab>=1){ |
|||
this.tabChange(this.currentTab-1) |
|||
}else if(this.start-end>100&&this.currentTab<this.TabList.length-1){ |
|||
this.tabChange(this.currentTab+1) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style lang="stylus"> |
|||
.tabs |
|||
position relative |
|||
height 100vh |
|||
display flex |
|||
flex-direction column |
|||
.active-switch |
|||
overflow scroll |
|||
.switch-container |
|||
position relative |
|||
display flex |
|||
flex-direction row |
|||
.focus-line |
|||
flex 1 |
|||
width 160upx |
|||
position absolute |
|||
bottom 0 |
|||
border-bottom 4upx solid #f07 |
|||
transition 0.3s |
|||
&.fix |
|||
width 250upx |
|||
.active-item |
|||
position relative |
|||
min-width 160upx |
|||
width 160upx |
|||
flex 1 |
|||
height 100upx |
|||
transition .3s |
|||
background-color: #fff |
|||
color #000 |
|||
text-align: center |
|||
display: flex |
|||
flex-direction: column |
|||
justify-content: space-around |
|||
border-bottom 1upx solid rgba(0,0,0,0.5) |
|||
&.focus |
|||
background #fff |
|||
color #f07 |
|||
transition-duration: .3s |
|||
&.fix |
|||
width 250upx |
|||
.item |
|||
// width: 220upx |
|||
padding: 0 5upx |
|||
overflow hidden |
|||
font-size: 28upx |
|||
.tab-pane-view |
|||
overflow hidden |
|||
background-color: #f7f7f7 |
|||
flex 1 |
|||
.tab-pane-group |
|||
display: block; |
|||
white-space: nowrap; |
|||
-webkit-transition: all .3s; |
|||
transition: all .3s; |
|||
width: 100%; |
|||
overflow: visible; |
|||
will-change: transform,left,top; |
|||
min-height 100upx |
|||
height 100% |
|||
.tab-pane-item |
|||
width: 100%; |
|||
min-height 100upx |
|||
display: inline-block |
|||
white-space: initial; |
|||
vertical-align: top; |
|||
font-size: 24upx; |
|||
box-sizing: border-box; |
|||
overflow: auto |
|||
</style> |
|||
@ -0,0 +1,64 @@ |
|||
```html |
|||
|
|||
<template> |
|||
<Tabs |
|||
:TabList="TabList" |
|||
:currentTab="current" |
|||
@tabs="tabsChange" |
|||
> |
|||
<TabPane> |
|||
<view>1</view> |
|||
</TabPane> |
|||
<TabPane> |
|||
<view>2</view> |
|||
</TabPane> |
|||
<TabPane> |
|||
<view>3</view> |
|||
</TabPane> |
|||
</Tabs> |
|||
</template> |
|||
<script> |
|||
/** |
|||
* 使用stylus预处理器,可能需要自己添加一下 |
|||
* 引入tabs 和tabPane组件 |
|||
为tabs 传入 |
|||
tablist(tab标题), |
|||
currentTab(选中的tab), |
|||
@tabs事件 改变选中的tabs |
|||
TabList:[ |
|||
{title:'商品介绍'}, |
|||
{title:'规格参数'}, |
|||
{title:'售后保障'} |
|||
] |
|||
TabPane 根据 tab的多少添加。 |
|||
更新: |
|||
在tabPane区域添加了滑动手指事件。横向滑动切换tab。 |
|||
在tabs.vue中的 tabChange方法中发布了全局事件,不需要可以注释掉。这里主要方便tabpane中的内容监听 tabs的切换做出响应。 |
|||
*/ |
|||
import Tabs from './components/tabs/tabs.vue' |
|||
import TabPane from './components/tabs/tabPane.vue' |
|||
|
|||
export default { |
|||
data(){ |
|||
return{ |
|||
current:0, |
|||
TabList:[ |
|||
{title:'商品介绍'}, |
|||
{title:'规格参数'}, |
|||
{title:'售后保障'} |
|||
] |
|||
} |
|||
}, |
|||
methods:{ |
|||
tabsChange(index){ |
|||
this.current = index |
|||
} |
|||
}, |
|||
components:{ |
|||
Tabs, |
|||
TabPane |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
``` |
|||
Loading…
Reference in new issue