【实用优雅】Vue+CSS徒手写选项卡样式的Tabs标签页

2,472 阅读3分钟

走过,路过,请各位看官,辛苦 **点赞 点赞 点赞**🙏🙏🙏

前言

写一个基于vue+css的Tabs标签页简单的Demo,不依赖其他前端组件和UI切图,方便jym复制即用,当然代码也存在很多待优化和提高的地方,我就抛个砖引玉前来优化!

在线预览Demo

Tab栏区域注意点

css绘制左右带弧角的选项卡背景 主要知识点用到::before ::after伪类来添加弧角

记得在伪类内添加 pointer-events: none; 鼠标穿透

image.png

Panel区域注意点

<TransitionGroup> 是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果

<TransitionGroup name="myAnimation" tag="ul"> 
  <li v-for="item in items" :key="item"> {{ item }} </li> 
</TransitionGroup>

自定义切换的过渡动画 架子我给jym搭好了 方便大家灵活自定义,当然也支持第三方css动画库

.myAnimation-enter-active {
  animation: fadeInRight 0.5s;
}
.myAnimation-leave-active {
  animation: fadeOutLeft 0.5s;
} 
@keyframes fadeInRight {
  from {
    opacity: 0;
    transform: translate3d(30%, 0, 0);
  } 
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}
@keyframes fadeOutLeft {
  from {
    opacity: 1;
  } 
  to {
    opacity: 0;
    transform: translate3d(-100%, 0, 0);
  }
}

完整代码

<template>
  <div class="layout-body">
    <!-- 面板模块 start -->
    <div class="easy-ui-panel">
      <!-- tab start -->
      <div class="easy-ui-tab">
        <div 
          class="easy-tab-item" 
          :class="{'tab-active-bar': activeTab==index}"
          v-for="(i,index) in tabList" 
          @click="doClick(i)"
          :key="index">
           {{i.label}}
        </div> 
      </div>
      <!-- tab end -->
      <!-- content start -->
      <div class="easy-ui-content">
        <TransitionGroup name="bounce" tag="ul">
          <!-- tabA start --> 
          <li class="easy-ui-main" key="0" v-if="activeTab==0">
            <!-- 内容 start -->
            <div class="easy-ui-default-bar">
              <img src="https://gd-hbimg.huaban.com/1165443b1582b4dfcb3f9c9ee1a634383273be045cc4-piLgzn"/>
              <p>{{ msg }}</p>
            </div>
            <!-- 内容 end --> 
          </li>   
          <!-- tabA end -->
          <!-- tabB start --> 
          <li class="easy-ui-main" key="1" v-if="activeTab==1">
            <!-- 内容 start -->
            <div class="easy-ui-default-bar">
              <img src="https://gd-hbimg.huaban.com/1165443b1582b4dfcb3f9c9ee1a634383273be045cc4-piLgzn"/>
              <p>{{ msg }}</p>
            </div>
            <!-- 内容 end --> 
          </li>   
          <!-- tabB end -->
          <!-- tabC start --> 
          <li class="easy-ui-main" key="2" v-if="activeTab==2">
            <!-- 内容 start -->
            <div class="easy-ui-default-bar">
              <img src="https://gd-hbimg.huaban.com/1165443b1582b4dfcb3f9c9ee1a634383273be045cc4-piLgzn"/>
              <p>{{ msg }}</p>
            </div>
            <!-- 内容 end --> 
          </li>   
          <!-- tabC end -->
          <!-- tabD start --> 
          <li class="easy-ui-main" key="3" v-if="activeTab==3">
            <!-- 内容 start -->
            <div class="easy-ui-default-bar">
              <img src="https://gd-hbimg.huaban.com/1165443b1582b4dfcb3f9c9ee1a634383273be045cc4-piLgzn"/>
              <p>{{ msg }}</p>
            </div>
            <!-- 内容 end --> 
          </li>   
          <!-- tabD end -->
        </TransitionGroup> 
      </div>
      <!-- content end -->
    </div>
    <!-- 面板模块 end -->
  </div>
</template>

<script>
// import Vue 不是必须的,需要手动指定 Vue 的版本时,可以解开注释
// import Vue from 'vue';

export default {
  data() {
    return {
      tabList:[
        {
          label:'人工智能',
          value:0
        },{
          label:'开发工具',
          value:1
        },{
          label:'代码人生',
          value:2
        },{
          label:'排行榜',
          value:3
        }
      ],
      activeTab:0, 
      msg:''
    };
  },
  created(){
    this.msg='当前内容为:'+this.tabList[0].label 
  },
  methods: {
    /**
     * Tab切换事件
     * @param data  点击Tab项  
     */
    doClick(data){    
      this.activeTab=data.value
      this.msg='当前内容为:'+data.label 
    }
  },
};
</script>
<style lang="scss">
html,body{
  margin: 0px;
  width: 100%;
  height: 100%;
  background: url('https://gd-hbimg.huaban.com/cdcae7be97f1e38e3f999f411db8977e72e39accd4752-UJe7BN');
  background-size: contain;
}

.bounce-enter-active {
  animation: fadeInRight 0.5s;
}
.bounce-leave-active {
  animation: fadeOutLeft 0.5s;
}
@keyframes fadeInRight {
  from {
    opacity: 0;
    transform: translate3d(30%, 0, 0);
  } 
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}
@keyframes fadeOutLeft {
  from {
    opacity: 1;
  } 
  to {
    opacity: 0;
    transform: translate3d(-100%, 0, 0);
  }
}
.layout-body{
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%; 
  box-sizing: border-box;  
  ul{
    margin: 0px;
    padding:0px;
    width: 100%;
    overflow: auto;
    li{
      display: block;
    }
    p{
      margin: 0px;
    }
  }

  @mixin noSelect {  
    user-select: none; /* 标准语法 */  
    -webkit-user-select: none; /* Chrome/Safari/Opera */  
    -moz-user-select: none; /* Firefox */  
    -ms-user-select: none; /* Internet Explorer/Edge */  
  }
  // 暂无数据 start ---------
  .easy-ui-default-bar{
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    align-items: center;
    justify-content: center;
    img{ height: 160px;}
    p{
      font-size: 12px;
      color:#c5c5c5;
    }
  }
  // 暂无数据 end ---------

  // Tab切换面板 start ----------------------
  .easy-ui-panel{
    display: flex;
    flex-direction: column;
    width: 600px;
    height: 300px;   

    $color-tab-text:#5b616c;
    $color-theme:#1e80ff;
    $color-tab-bg:#1e80ff;
    $card-radius:7px;

    // 定义关键帧动画
    @keyframes animationShiny {
      0% {opacity: 1;}
      50% {opacity: 0.4;}
      100% {opacity: 1;}
    }

    *{
      box-sizing: border-box;
      font-size: 12px;
    }

    // tab切换区域css
    .easy-ui-tab{
      width: 100%;
      height: 40px; 
      display: flex;
      flex-shrink: 0; 

      background: $color-tab-bg;
      border-radius: $card-radius $card-radius 0px 0px;

      .easy-tab-item{
        position: relative; 
        display: block;
        width: 100px;
        height: 100%;
        line-height: 35px;
        color:#fff; 
        font-size: 12px;
        cursor: pointer;
        text-align: center; 
        z-index: 3; 
        @include noSelect;
      }
      
      $circleSize:32px;
      @mixin iconBox{
        content: "";
        position: absolute;
        bottom:-46px; 
        width:$circleSize;
        height:$circleSize;
        border:8px solid transparent;
        border-radius:100%; 
        z-index:1;
      }
      .tab-active-bar{
        font-size: 13px;
        font-weight:700;
        color:$color-theme;
        line-height: 45px !important;
        border-bottom:45px solid #fff; 
        border-right: 10px solid transparent;
        border-radius: 8px 18px 0px 0px;
        margin-top: -3px;
        user-select:none;
        filter: drop-shadow(3px 0px 5px rgba(#07408b,0.3));

        &:first-child{
           &::after {
              @include iconBox;
              right:-45px; 
              border-left-color:#fff; 
              transform:rotate(-45deg);
              pointer-events: none;
          } 
        }

        &:not(:first-child){ 
          border-left: 7px solid transparent;
          border-right: 7px solid transparent !important;
          border-radius: 18px 18px 0px 0px !important;

            &::before {
              @include iconBox;
              left:-43px; 
              border-right-color:#fff;
              transform:rotate(45deg);  
              pointer-events: none;
            }
            &::after {
              @include iconBox;
              right:-43px; 
              border-left-color:#fff; 
              transform:rotate(-45deg);
              pointer-events: none;
            } 
        } 
      }  
    }
    // 内容区域css
    .easy-ui-content{  
      position: relative;
      width: 100%;
      height: 0px;
      flex-grow: 1;
      border-radius: 5px;
      margin-top: -5px;
      padding:20px;
      z-index: 4;
      background: #fff;
      box-shadow: 0px 4px 4px rgba(#999, 0.04),
                  0px 8px 8px rgba(#999, 0.08);
      overflow: hidden;

      .easy-ui-main{
        position:absolute;
        width: calc(100% - 40px);
        left: 20px;
        top:20px;  
      } 
    } 
  } 
  // Tab切换面板 end ----------------------
} 
</style>