vue側(cè)欄組件實(shí)現(xiàn)

1.效果預(yù)覽:

效果圖

2. 已實(shí)現(xiàn)功能:

  1. 點(diǎn)擊頂部工具欄,打開側(cè)欄
  2. 點(diǎn)擊關(guān)閉按鈕,關(guān)閉側(cè)欄
  3. 點(diǎn)擊頂部不同工具欄,切換側(cè)欄內(nèi)容
  4. 箭頭指向點(diǎn)擊位置
  5. 側(cè)欄入場和出場動畫

3. 功能分析

3.1 遮罩效果

將div的背景設(shè)置成黑色,透明度0.5,并設(shè)置z-index使其可以蓋在主體頁面之上

.cover {
  position: fixed;
  left: 0;
  top: 50px;
  width: 100%;
  height: 100%;
  background: #000000;
  opacity: 0.5;
  z-index: 500;
}

3.2 箭頭實(shí)現(xiàn)

  1. 通過css畫一個白色背景到三角形
  2. 通過 :style="{right:position} 控制right的位置實(shí)現(xiàn)其移動
  3. 通過調(diào)用"openSlider('message','130px')" 傳遞合適的值
 <div class="pointer">
      <div ref="triangle" class="triangle" :style="{right:position}" />
 </div>

<style lang="scss" scoped>
.pointer {
  height: 14px;
  width: 100%;
  background: transparent;
  position: relative;
}

.triangle{
    width: 0;
    height: 0;
    border: 7px solid transparent;
    border-bottom-color: #fff ;
    position: absolute;
    top: 0;
}
</style>

3.3 動畫實(shí)現(xiàn)

  1. 通過 <transition> 標(biāo)簽將需要進(jìn)行動畫的元素進(jìn)行包裹, vue會添加如下到class
  • v-enter:定義進(jìn)入過渡的開始狀態(tài)。在元素被插入之前生效,在元素被插入之后的下一幀移除。
  • v-enter-active:定義進(jìn)入過渡生效時的狀態(tài)。在整個進(jìn)入過渡的階段中應(yīng)用,在元素被插入之前生效,在過渡/動畫完成之后移除。這個類可以被用來定義進(jìn)入過渡的過程時間,延遲和曲線函數(shù)。
  • v-enter-to: 2.1.8版及以上 定義進(jìn)入過渡的結(jié)束狀態(tài)。在元素被插入之后下一幀生效 (與此同時 v-enter 被移除),在過渡/動畫完成之后移除。
  • v-leave: 定義離開過渡的開始狀態(tài)。在離開過渡被觸發(fā)時立刻生效,下一幀被移除。
  • v-leave-active:定義離開過渡生效時的狀態(tài)。在整個離開過渡的階段中應(yīng)用,在離開過渡被觸發(fā)時立刻生效,在過渡/動畫完成之后移除。這個類可以被用來定義離開過渡的過程時間,延遲和曲線函數(shù)。
  • v-leave-to: 2.1.8版及以上 定義離開過渡的結(jié)束狀態(tài)。在離開過渡被觸發(fā)之后下一幀生效 (與此同時 v-leave 被刪除),在過渡/動畫完成之后移除。

name屬性定義了需要調(diào)用動畫到前置,如我這里設(shè)置了name = "my",那么相應(yīng)的自定義動畫將前綴v改成my

@keyframes lightSpeedIn {
  from {
    transform: translate3d(100%, 0, 0);
    opacity: 0;
  }

  to {
    transform: none;
    opacity: 1;
  }
}

.my-enter-active  {
  transform-origin: left center;
   animation: lightSpeedIn 0.4s;
}

@keyframes lightSpeedOut {
  from {
    opacity: 1;
  }

  to {
    transform: translate3d(100%, 0, 0);
    opacity: 0;
  }
}

.my-leave-active {
  transform-origin: left center;
   animation: lightSpeedOut 0.4s;
}

3.4 側(cè)欄內(nèi)容切換

通過v-show控制當(dāng)前顯示的組件

<todo v-show="openIndex === 'todo'" />
<message v-show="openIndex === 'message'" />

4.代碼展示

rightslide.vue

<template>
  <div>
    <div v-show="openIndex" class="cover" />

    <transition name="my">
      <div v-show="openIndex" class="slider-container">
        <div class="pointer">
          <div ref="triangle" class="triangle" :style="{right:position}" />
        </div>
        <div class="slider-content">
          <div>
            <span @click="closeSlide">X</span>
          </div>
          <todo v-show="openIndex === 'todo'" />
          <message v-show="openIndex === 'message'" />
        </div>
      </div>
    </transition>
  </div>
</template>

<script>
import Todo from '@/views/todo'
import Message from '@/views/message'
export default {
  components: {
    Todo,
    Message
  },
  data() {
    return {
      show: true
    }
  },
  computed: {
    openIndex() {
      return this.$store.state.rightSlider.openIndex
    },
    position() {
      return this.$store.state.rightSlider.trianglePosition
    }
  },
  methods: {
    closeSlide() {
      this.$store.dispatch('closeSlide')
    },
    handleClick: function() {
      this.show = !this.show
    }
  }
}
</script>

<style lang="scss" scoped>
.cover {
  position: fixed;
  left: 0;
  top: 50px;
  width: 100%;
  height: 100%;
  background: #000000;
  opacity: 0.5;
  z-index: 500;
}

.slider-container {
  width: 506px;
  height: 100%;
  top: 36px;
  right: 0px;
  position: fixed;
  z-index: 1000;
}

.slider-content {
  background: #fff;
  height: 100%;
}

.pointer {
  height: 14px;
  width: 100%;
  background: transparent;
  position: relative;
}

.triangle{
    width: 0;
    height: 0;
    border: 7px solid transparent;
    border-bottom-color: #fff ;
    position: absolute;
    top: 0;
}

@keyframes lightSpeedIn {
  from {
    transform: translate3d(100%, 0, 0);
    opacity: 0;
  }

  to {
    transform: none;
    opacity: 1;
  }
}

.my-enter-active  {
  transform-origin: left center;
   animation: lightSpeedIn 0.4s;
}

@keyframes lightSpeedOut {
  from {
    opacity: 1;
  }

  to {
    transform: translate3d(100%, 0, 0);
    opacity: 0;
  }
}

.my-leave-active {
  transform-origin: left center;
   animation: lightSpeedOut 0.4s;
}
</style>

vuex

const state = {
    openIndex: '', // 標(biāo)識側(cè)欄顯示的組件
    trianglePosition: '' // 側(cè)欄指向按鈕距離右邊到距離
}

const mutations = {
  changeIndex(state, { index, position }) {
    state.openIndex = index
    state.trianglePosition = position
  },
  closeSlide(state) {
    state.openIndex = ''
  },
  setPosition(state, position) {
    state.trianglePosition = position
  }
}

const actions = {
  changeIndex({ commit }, { index, position }) {
    commit('changeIndex', { index, position })
  },
  closeSlide({ commit }) {
    commit('closeSlide')
  },
  setPosition({ commit }, { position }) {
    commit('setPosition', position)
  }
}

export default {
  state,
  mutations,
  actions
}

調(diào)用:

<template>
<div>
  <span @click="openSlider('message','130px')">消息中心</span>
    <span @click="openSlider('todo','210px')">待辦事項(xiàng)</span>
</div>
</template>
<script>
methods: {
openSlider(value, position) {
      this.$store.dispatch('changeIndex', { index: value, position: position })
    }
  }
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容