<div id="app">
<vue-tabs>
<vue-tab label="first">
<h1>First</h1>
<div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit, corporis!</div>
</vue-tab>
<vue-tab label="second">
<h1>Second</h1>
<div>Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugit, corporis! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Asperiores voluptatum, dolore provident sequi incidunt mollitia odit minus facere itaque reprehenderit minima aperiam nihil numquam veniam porro assumenda doloribus quidem id!</div>
</vue-tab>
<vue-tab label="third" :active="true">
<h1>Third</h1>
<div>Далеко-далеко за словесными горами в стране гласных, и согласных живут рыбные тексты. Жаренные свой гор деревни о всеми, бросил власти одна коварный имени, над, несколько путь которой щеке предложения залетают вершину первую.</div>
</vue-tab>
</vue-tabs>
</div>
*
box-sizing: border-box
body
font-family: sans-serif
padding: 0 15px
background: #f4f4f4
.tabs
margin: 50px auto
max-width: 700px
background: #fff
box-shadow: 0 3px 16px rgba(#000, .1)
&__list
display: flex
height: 40px
border-bottom: solid 1px #e9e9e9
&__tab
padding: 0 14px
display: flex
align-items: center
color: rgba(#000, .7)
font-size: 13px
text-transform: uppercase
cursor: pointer
&_active
color: dodgerblue
&__container
position: relative
overflow: hidden
will-change: height
transition: height .4s cubic-bezier(.86,0,.07,1)
&__wrapper
display: flex
justify-content: flex-start
align-items: flex-start
will-change: transform
transition: transform .4s cubic-bezier(.86,0,.07,1)
&__content
flex: 1 0 100%
width: 100%
padding: 16px
View Compiled
console.clear()
Vue.component('vue-tabs', {
template: `<div class="tabs">
<div class="tabs__list">
<div
class="tabs__tab"
:class="{'tabs__tab_active': i === active}"
@click="changeTab(i)"
v-for="(label, i) in Tabs.labels"
:key="i"
>{{label}}</div>
</div>
<div class="tabs__container" ref="container">
<div class="tabs__wrapper" ref="wrapper">
<slot />
</div>
</div>
</div>`,
computed: {
active() {
return this.Tabs.active
}
},
data() {
return {
Tabs: {
labels: [],
items: [],
active: 0
}
}
},
methods: {
setHeight() {
const el = this.Tabs.items[this.active]
const height = el.scrollHeight
this.$refs.container.style.height = height + 'px'
},
scrollWrapper() {
const {wrapper} = this.$refs
const offset = 100 * this.active
wrapper.style.transform = `translate3d(-${offset}%, 0, 0)`
},
changeTab(i) {
this.Tabs.active = i
this.setHeight()
this.scrollWrapper()
}
},
provide() {
return {
Tabs: this.Tabs
}
},
mounted() {
this.setHeight()
this.scrollWrapper()
this.debounce = _.debounce(this.setHeight, 50)
window.addEventListener('resize', this.debounce)
},
beforeDestroy() {
window.removeEventListener('resize', this.debounce)
}
})
Vue.component('vue-tab', {
template: `<div class="tabs__content"><slot /></div>`,
props: ['label', 'active'],
inject: ['Tabs'],
created() {
const length = this.Tabs.labels.push(this.label)
if (this.active) {
this.Tabs.active = length - 1
}
},
mounted() {
this.Tabs.items.push(this.$el)
}
})
new Vue({
el: '#app'
})
View Compiled
This Pen doesn't use any external CSS resources.