Skip to content

Instantly share code, notes, and snippets.

@edwardlorilla
Created May 25, 2021 17:02
Show Gist options
  • Save edwardlorilla/8184271ae61b29ba059066deb792ed7e to your computer and use it in GitHub Desktop.
Save edwardlorilla/8184271ae61b29ba059066deb792ed7e to your computer and use it in GitHub Desktop.
jOBwOzM
<div id="app">
<div class="main-box">
<button @click="add">Add</button>
<div class="main-box-tab">
<i @click="previous"><<</i>
<i @click="next">>></i>
<div class="main-box-tab-content" ref="tabs">
<div class="main-box-tab-roll">
<div v-for="(item,index) in tabs" :key="index"
:class="{'tab-item-action':actionName === item.name ,'tab-item':actionName !== item.name}"
@click.stop="clickTab(item.name,index)">
<span>{{item.meta.title}}</span>
<i class="el-icon-close" @click.stop="close(item.name)"></i>
</div>
</div>
</div>
</div>
<div class="main-box-content">
<div>{{actionName}}</div></b>
</div>
</div>
</div>
new Vue( {
name: "index",
data() {
return {
tabs: [],
moveX: 0,
count: 1,
unoccupied: 0,
tabsCount: 0,
actionName: 'test1'
}
},
watch: {
actionName(val) {
let len = this.tabs.length
// If there is duplicate data, exit the subsequent function execution
for (let i = 0; i <len; i++) {
if (this.tabs[i].name === val) {
this.$nextTick(() => {
this.translateX((i + 1-this.tabsCount) * this.width-this.unoccupied)
})
return
}
}
this.tabs.push({
name: `test${this.count}`,
meta: {
title: `test${this.count}`
}
})
this.$nextTick(() => {
// (How many tabs are there in total-the number of elements visible when not offset) * The length of a single tab tag element-the width of the visible part of the obscured tab element
this.translateX((this.tabs.length-this.tabsCount) * this.width-this.unoccupied)
})
}
},
mounted() {
this.tabs.push({
name: `test${this.count}`,
content: 'lorem ipsum',
meta: {
title: `test${this.count}`
}
})
this.$nextTick(() => {
let tabs = this.$refs.tabs
let getStyle = getComputedStyle(tabs.children[0].children[0], null)
let marginLeft = parseFloat(getStyle.marginLeft.substr(0, getStyle.marginLeft.length - 2))
let marginRight = parseFloat(getStyle.marginRight.substr(0, getStyle.marginRight.length - 2))
// The actual width of the element = the width of the element + margin
this.width = marginLeft + marginRight + tabs.children[0].children[0].offsetWidth
// The final reduction is to take the remainder (the number is the same as the above calculation)
this.unoccupied = tabs.offsetWidth% this.width
// Convert to integer
this.tabsCount = parseInt(tabs.offsetWidth / this.width)
})
},
methods: {
add() {
this.count++
this.actionName = `test${this.count}`
},
/**
* Switch tab page
**/
clickTab(name) {
if (this.actionName !== name) {
this.actionName = name
}
},
/**
* Close the tab page
**/
close(name) {
let len = this.tabs.length
let jumpName = null
if (len > 1) {
for (let i = 0; i < len; i++) {
if (this.tabs[i].name === name) {
this.tabs.splice(i, 1)
jumpName = this.tabs[i ? i - 1 : 0].name
if (this.actionName !== jumpName && name === this.actionName) {
this.actionName = jumpName
}
this.$nextTick(() => {
this.previous()
})
return
}
}
}
},
/**
* Offset to the right
**/
next() {
// scrollWidth acquisition is inaccurate
// Use this.width * this.tabs.length to calculate the total length
let totalWidth = this.width * this.tabs.length
this.$nextTick(() => {
let dom = this.$refs.tabs
// Viewable area <scrolling area (the scrolling area is larger than the visible area to move)
// Moving distance + visible area = width of scrolling area (the last width, when clicked is the actual width) <scrolling area
if (dom.clientWidth <totalWidth && this.moveX + dom.clientWidth <totalWidth) {
// this.moveX is 0 minus the width of the free space
this.moveX += this.moveX? this.width: this.width-this.unoccupied
this.translateX(this.moveX)
}
})
},
/**
* Offset to the left
**/
previous() {
if (this.moveX> 0) {
this.moveX -= this.width
this.translateX(this.moveX)
}
},
/**
* Start moving dom
**/
translateX(x) {
this.moveX = x <0? 0: x
this.$refs.tabs.children[0].style.transform = `translateX(-${this.moveX}px)`
}
}
}).$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
.main-box {
height: 500px;
width: 500px;
padding: 10px 20px 20px 20px;
.main-box-tab {
position: relative;
padding: 10px 20px;
overflow: hidden;
& > i {
position: absolute;
cursor: pointer;
bottom: 15px;
&:nth-child(1) {
left: 0;
}
&:nth-child(2) {
right: 0;
}
}
.main-box-tab-content {
overflow: hidden;
.main-box-tab-roll {
transition: transform .5s;
display: flex;
align-items: center;
div {
flex-shrink: 0;
cursor: pointer;
width: 130px;
height: 25px;
margin: 0 5px;
display: flex;
align-items: center;
justify-content: space-between;
span, i {
font-size: 12px;
}
span {
margin-left: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
i {
margin-right: 10px;
}
}
}
}
.tab-item {
color: #cccccc;
background-color: rgba(255, 255, 255, .5);
border-radius: 0 1px 0 1px;
border: 1px solid #052141;
}
.tab-item-action {
color: #ffffff;
background: rgba(0, 180, 255, 0.8);
border-radius: 0 1px 0 1px;
border: 1px solid #1E2088;
}
}
.main-box-content {
height: calc(100% - 70px);
padding: 10px;
border: 1px saddlebrown solid;
background-size: 100% 100%;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment