×

vue开发每日一学:多个tab标签的滑动展示效果

作者:天空2021.05.25来源:Web前端之家浏览:6595评论:0
关键词:vuejs

vue开发每日一学:多个tab标签的滑动展示效果。当创建的tab标签超出页面可视区域时自动滚动一个tab标签距离,并可手动点击滚动tab标签。

  1. <template>
  2.     <div>
  3.         <button @click="add">添加</button>
  4.         <div>
  5.             <i @click="previous"><<</i>
  6.             <i @click="next">>></i>
  7.             <div ref="tabs">
  8.                 <div>
  9.                     <div v-for="(item,index) in tabs" :key="index"
  10.                          :class="{'tab-item-action':actionName === item.name ,'tab-item':actionName !== item.name}"
  11.                          @click.stop="clickTab(item.name,index)">
  12.                         <span>{{item.meta.title}}</span>
  13.                         <i @click.stop="close(item.name)"></i>
  14.                     </div>
  15.                 </div>
  16.             </div>
  17.         </div>
  18.         <div>
  19.             <div>{{actionName}}</div>
  20.         </div>
  21.     </div>
  22. </template>
  23. <script>
  24.     export default {
  25.         name: "index",
  26.         data() {
  27.             return {
  28.                 tabs: [],
  29.                 moveX: 0,
  30.                 count: 1,
  31.                 unoccupied: 0,
  32.                 tabsCount: 0,
  33.                 actionName: 'test1'
  34.             }
  35.         },
  36.         watch: {
  37.             actionName(val) {
  38.                 let len = this.tabs.length
  39.                 // 如有重复数据退出后续函数执行
  40.                 for (let i = 0; i < len; i++) {
  41.                     if (this.tabs[i].name === val) {
  42.                         this.$nextTick(() => {
  43.                             this.translateX((+ 1 - this.tabsCount) * this.width - this.unoccupied)
  44.                         })
  45.                         return
  46.                     }
  47.                 }
  48.  
  49.                 this.tabs.push({
  50.                     name: `test${this.count}`,
  51.                     meta: {
  52.                         title: `test${this.count}`
  53.                     }
  54.                 })
  55.                 this.$nextTick(() => {
  56.                   // (总共有多少个tabs - 未偏移时可见的元素个数) * 单个tab标签元素长度 - 被遮挡tab元素的可见部分的宽度
  57.                     this.translateX((this.tabs.length - this.tabsCount) * this.width - this.unoccupied)
  58.                 })
  59.             }
  60.         },
  61.         mounted() {
  62.             this.tabs.push({
  63.                 name: `test${this.count}`,
  64.                 meta: {
  65.                     title: `test${this.count}`
  66.                 }
  67.             })
  68.             this.$nextTick(() => {
  69.                 let tabs = this.$refs.tabs
  70.                 let getStyle = getComputedStyle(tabs.children[0].children[0], null)
  71.                 let marginLeft = parseFloat(getStyle.marginLeft.substr(0, getStyle.marginLeft.length - 2))
  72.                 let marginRight = parseFloat(getStyle.marginRight.substr(0, getStyle.marginRight.length - 2))
  73.                 // 元素实际宽度 = 元素的宽度 + 外边距
  74.                 this.width = marginLeft + marginRight + tabs.children[0].children[0].offsetWidth
  75.  
  76.                 /**
  77.                  * 以下注释计算方式用于理解实现逻辑
  78.                  **/
  79.                 // // 可视区域能放入多少个元素 = 可视区域的宽度 / 子元素实际宽度
  80.                 // let num = tabs.offsetWidth / this.width
  81.  
  82.                 // // 被遮挡tab元素的可见部分的宽度 = 可见区域的宽度 - (子元素实际宽度 * num转为整数)
  83.                 // this.unoccupied = tabs.offsetWidth - (this.width * parseInt(num))
  84.  
  85.                 // 最终精简为取余(得数跟上面的计算是一样的)
  86.                 this.unoccupied = tabs.offsetWidth % this.width
  87.                 // 转为整数
  88.                 this.tabsCount = parseInt(tabs.offsetWidth / this.width)
  89.             })
  90.         },
  91.         methods: {
  92.             add() {
  93.                 this.count++
  94.                 this.actionName = `test${this.count}`
  95.             },
  96.  
  97.             /**
  98.              * 切换tab标签页
  99.              **/
  100.             clickTab(name) {
  101.                 if (this.actionName !== name) {
  102.                     this.actionName = name
  103.                 }
  104.             },
  105.  
  106.             /**
  107.              * 关闭tab标签页
  108.              **/
  109.             close(name) {
  110.                 let len = this.tabs.length
  111.                 let jumpName = null
  112.                 if (len > 1) {
  113.                     for (let i = 0; i < len; i++) {
  114.                         if (this.tabs[i].name === name) {
  115.                             this.tabs.splice(i, 1)
  116.  
  117.                             jumpName = this.tabs[? i - 1 : 0].name
  118.                             if (this.actionName !== jumpName && name === this.actionName) {
  119.                                 this.actionName = jumpName
  120.                             }
  121.  
  122.                             this.$nextTick(() => {
  123.                                 this.previous()
  124.                             })
  125.                             return
  126.                         }
  127.                     }
  128.                 }
  129.             },
  130.  
  131.             /**
  132.              * 往右偏移
  133.              **/
  134.             next() {
  135.                 // scrollWidth获取不准确
  136.                 // 使用this.width * this.tabs.length计算出总长度
  137.                 let totalWidth = this.width * this.tabs.length
  138.  
  139.                 this.$nextTick(() => {
  140.                     let dom = this.$refs.tabs
  141.                     // 可视区域 < 滚动区域(滚动区域大于可视区域才可以移动)
  142.                     // 移动距离 + 可视区域 = 滚动区域的宽度(上一次的宽度,当点击时才是实际宽度)< 滚动区域
  143.                     if (dom.clientWidth < totalWidth && this.moveX + dom.clientWidth < totalWidth) {
  144.                         // this.moveX为0减去空余空间的宽度
  145.                         this.moveX += this.moveX ? this.width : this.width - this.unoccupied
  146.                         this.translateX(this.moveX)
  147.                     }
  148.                 })
  149.             },
  150.  
  151.             /**
  152.              * 往左偏移
  153.              **/
  154.             previous() {
  155.                 if (this.moveX > 0) {
  156.                     this.moveX -= this.width
  157.                     this.translateX(this.moveX)
  158.                 }
  159.             },
  160.  
  161.             /**
  162.              * 开始移动dom
  163.              **/
  164.             translateX(x) {
  165.                 this.moveX = x < 0 ? 0 : x
  166.                 this.$refs.tabs.children[0].style.transform = `translateX(-${this.moveX}px)`
  167.             }
  168.         }
  169.     }
  170. </script>
  171.  
  172. <style scoped>
  173.     .main-box {
  174.         height: 500px;
  175.         width: 500px;
  176.         padding: 10px 20px 20px 20px;
  177.  
  178.         .main-box-tab {
  179.             position: relative;
  180.             padding: 10px 20px;
  181.             overflow: hidden;
  182.  
  183.             & > i {
  184.                 position: absolute;
  185.                 cursor: pointer;
  186.                 bottom: 15px;
  187.  
  188.                 &:nth-child(1) {
  189.                     left: 0;
  190.                 }
  191.  
  192.                 &:nth-child(2) {
  193.                     right: 0;
  194.                 }
  195.             }
  196.  
  197.             .main-box-tab-content {
  198.                 overflow: hidden;
  199.  
  200.                 .main-box-tab-roll {
  201.                     transition: transform .5s;
  202.                     display: flex;
  203.                     align-items: center;
  204.  
  205.                     div {
  206.                         flex-shrink: 0;
  207.                         cursor: pointer;
  208.                         width: 130px;
  209.                         height: 25px;
  210.                         margin: 0 5px;
  211.                         display: flex;
  212.                         align-items: center;
  213.                         justify-content: space-between;
  214.  
  215.                         span, i {
  216.                             font-size: 12px;
  217.                         }
  218.  
  219.                         span {
  220.                             margin-left: 10px;
  221.                             overflow: hidden;
  222.                             white-space: nowrap;
  223.                             text-overflow: ellipsis;
  224.                         }
  225.  
  226.                         i {
  227.                             margin-right: 10px;
  228.                         }
  229.                     }
  230.                 }
  231.             }
  232.  
  233.             .tab-item {
  234.                 color: #cccccc;
  235.                 background-color: rgba(255, 255, 255, .5);
  236.                 border-radius: 0 1px 0 1px;
  237.                 border: 1px solid #052141;
  238.             }
  239.  
  240.             .tab-item-action {
  241.                 color: #ffffff;
  242.                 background: rgba(0, 180, 255, 0.8);
  243.                 border-radius: 0 1px 0 1px;
  244.                 border: 1px solid #1E2088;
  245.             }
  246.  
  247.         }
  248.  
  249.         .main-box-content {
  250.             height: calc(100% - 70px);
  251.             padding: 10px;
  252.             border: 1px saddlebrown solid;
  253.             background-size: 100% 100%;
  254.         }
  255.     }
  256. </style>

大家可以放到vue开发软件里试试哦。

您的支持是我们创作的动力!
温馨提示:本文作者系 ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/vuejs20210525a1.html

网友评论文明上网理性发言 已有0人参与

发表评论: