Skip to content

Instantly share code, notes, and snippets.

@dagalti
Last active June 11, 2024 14:37
Show Gist options
  • Save dagalti/eea7f49378d9fa21de5e359dde0f859a to your computer and use it in GitHub Desktop.
Save dagalti/eea7f49378d9fa21de5e359dde0f859a to your computer and use it in GitHub Desktop.
Vuejs Material design tabs with swipe, transitions effects without using extra libraries.
<!---
1. Set Tabwidth in data
2. Tabs are swipeable
3. Scrollable tabs
4. No external libraries used expcept vuejs
5. Using vuejs transition for animation
Demo : https://dagalti.github.io/vue-tabs.html
Codepen : https://codepen.io/dagalti/pen/pYKXQW
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>Vue Swipe Tabs</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500" rel="stylesheet">
<style>
.slide-next-leave-active, .slide-next-enter-active, .slide-prev-enter-active, .slide-prev-leave-active {
transition: .5s;
}
.slide-next-enter,.slide-next-leave, .slide-prev-leave-to
{
transform: translate(100%, 0);
}
.slide-next-leave-to, .slide-prev-enter, .slide-prev-leave {
transform: translate(-100%, 0);
}
.tabs{
display:flex;
position:relative;
background:#1565c0;
color: #f1f1f1;
height:48px;
box-shadow:0 3px 3px -2px rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 1px 7px 0 rgba(0,0,0,.12);
overflow-x:scroll;
overflow: -moz-scrollbars-none;
-ms-overflow-style: none;
}
.tabs::-webkit-scrollbar {
width: 0 !important;
height:0 !important;
}
.tabitem{
display:flex;
align-items:center;
justify-content:center;
min-width:var(--tabwidth);
cursor:pointer;
text-transform:uppercase;
font-size:14px;
}
.tabitem.active{
font-weight: 500;
color: white;
}
.slider{
position:absolute;
bottom:0px;
height:2px;
width:var(--tabwidth);
background:white;
transition:.5s ease;
}
.tabcontainer {
height:500px;
position: relative;
min-height: 100%;
width: 100%;
touch-action:pan-y;
}
.tabpane{
position: absolute;
width: 100%;
font-size:32px;
display:flex;
align-items:center;
justify-content:center;
height:300px;
}
#app{
margin:0 auto;
text-align:center;
}
body{
font-family: "Roboto", Helvetica, sans-serif;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
margin:0;
}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<div class="tabs" ref="tabbar">
<div class="tabitem" :class="index === activetab ? 'active' : ''" v-for="(tab, index) in items" @click="switchtab(index)" :key="index" ref="tab">{{tab}}</div>
<div class="slider" :style="'transform:translateX('+activetab*tabwidth+'px)'"></div>
</div>
<div ref="tcon" class="tabcontainer" v-on="pointer
?{pointerdown: ($event)=>start($event), pointermove: ($event)=>move($event), pointerup: ($event)=>end($event)}
:{touchstart: ($event)=>start($event), touchmove: ($event)=>move($event), touchend: ($event)=>end($event)}"
>
<transition :name="transition" v-for="(tab, index) in items" :key="index">
<div class="tabpane" v-if="index === activetab">
{{tab}} : Tab Content
</div>
</transition>
</div>
<h2>Source Code : </h2> <a href="https://gist.github.com/dagalti/eea7f49378d9fa21de5e359dde0f859a" target="blank">Vue Simple Swipable Material Tabs</a>
</div>
<script>
window.app = new Vue({
el:'#app',
data() {
return {
transition: "slide-next",
activetab: 0,
tabwidth: 135,
items:['Google', 'Apple', 'Microsoft', 'Samsung', 'Nokia', 'Sony', 'Vivo', 'Oneplus', 'Oppo', 'LG', 'Blackberry'],
touch:{sx:null, sy: null, st:null, ex:null, ey:null, et:null}
}
},
mounted(){
this.$refs.tabbar.style.setProperty('--tabwidth', this.tabwidth+'px')
},
computed:{
pointer(){
if(window.PointerEvent) return true
else return false
}
},
methods: {
switchtab(n){
let scroll, scond
if(this.activetab>n){
this.transition = "slide-prev"
scroll = n-1
if(scond)this.$refs.tab[scroll].scrollIntoView({behavior:'smooth'})
}else if(this.activetab<n){
this.transition = "slide-next"
scroll = n+1
}
scond = scroll>=0 && scroll < this.items.length
if(scond)this.$refs.tab[scroll].scrollIntoView({behavior:'smooth'})
Vue.nextTick(_ => {
this.activetab = n
})
},
start(e){
this.settouchpos(e, 'start')
},
move(e){
this.settouchpos(e, 'move')
},
end(e){
this.settouchpos(e, 'end')
let dx = this.touch.sx - this.touch.ex,
dy = this.touch.sy - this.touch.ey,
dt = this.touch.et - this.touch.st,
dir = Math.sign(dx),
ntab = this.activetab+dir,
vmove = Math.abs(dx) / Math.abs(dy) < Math.sqrt(4)
ntab = ntab >=0 && ntab<this.items.length ? ntab : null
if(Math.abs(dx)>10 && ntab !==null && !vmove && dt<300) this.switchtab(ntab)
},
settouchpos(e, event){
let ev = e.changedTouches ? e.changedTouches[0] : e
if(event === 'start'){
this.touch.sx = ev.clientX
this.touch.sy = ev.clientY
this.touch.st = Date.now()
}else{
this.touch.ex = ev.clientX
this.touch.ey = ev.clientY
this.touch.et = Date.now()
}
}
}
})
</script>
</body>
</html>
@mavinoo
Copy link

mavinoo commented Jun 15, 2020

not work move up & down in list!!
(scroll not work)

@mavinoo
Copy link

mavinoo commented Jun 15, 2020

change

.tabcontainer {
   ...
  touch-action:none;
}

to

.tabcontainer {
   ...
  touch-action:pan-y;
}

tnx ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment