Vue Snackbar 消息條隊列顯示,依次動畫消失的實現


效果預覽

思路

  1. 封裝 Snackbar 組件;
  2. 在根路由頁面下建立全局 Snackbar 控制器,統一管理 Snackbar;
  3. 通過事件通知全局 Snackbar 控制器顯示消息;

實現

1. 封裝 Snackbar 組件

project/src/components/snackbar.vue

<template>
  <div class="component-snackbar" v-if="value">
    <div class="snackbar-content">{{ message }}</div>
    <div class="snackbar-close" v-if="closable" @click="close">關閉</div>
  </div>
</template>

<script>
export default {
  name: "component-snackbar",
  props: {
    value: Boolean, // 調用本組件v-model傳入的值
    message: String, // 消息內容
    closable: { // 是否顯示刪除按鈕
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      showTime: 6000, // snackbar顯示的時長
      timer: null // 定時器
    }
  },
  mounted() {
    // 在 showTime 到期后自動關閉snackbar
    this.timer = setTimeout(() => {
      this.close()
    }, this.showTime)
  },
  methods: {
    close() {
      // 清除定時器
      clearTimeout(this.timer)
      // 不能直接在組件中修改props中的數據,因此不能直接修改this.value = false
      // 而是實現了在自定義組件中使用v-model,通過外傳 input 事件通知調用者自動更新 v-model 傳入的值
      this.$emit('input', false)
    }
  }
}
</script>

<style lang="less">
.component-snackbar {
  width: 400px;
  height: 60px;
  position: fixed;
  right: 10px;
  bottom: 10px;
  display: flex;
  flex-direction: row;
  align-items: center;
  border-radius: 8px;
  background-color: #333333;
  box-shadow: 2px 4px 6px 0 rgba(0,0,0,.4);
  transition: transform 500ms ease-in;
  .snackbar-content {
    flex: 1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    padding: 0 16px;
  }
  .snackbar-close {
    margin: 0 16px;
    color: deeppink;
    cursor: pointer;
  }
}
</style>

2. 全局 Snackbar 控制器

注冊事件總線 EventBus;

project/src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

// 創建 Vue 的實例作為事件總線使用,將其掛載到 $eventBus 上,
// 即可在組件中直接使用 this.$eventBus.$emit() 和 this.$eventBus.$on() 來觸發/監聽事件
Vue.prototype.$eventBus = new Vue()

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

全局 Snackbar 控制器

全局 Snackbar 控制器在根路由頁面下監聽 showSnackbar 事件,並維護一個消息列表,通過對事件的監聽有序的推入、刪除消息,從而達到控制 Snackbar 顯示的目的;

project/src/App.vue

<template>
  <div id="app">
    <router-view />

    <!-- 全局 Snackbar 隊列 -->
    <!-- 根據當前 Snackbar 的 index 動態計算 Y 方向的位移,達到依次排列的目的 -->
    <snackbar
      v-model="item.show"
      :style="{transform: 'translateY(' + -(60+10) * index + 'px)'}"
      :message="item.content"
      :closable="item.closable"
      v-for="(item, index) in messages"
      :key="item.id"
      @input="onSnackbarClose($event, index)"></snackbar>
  </div>
</template>

<script>
import Snackbar from './components/snackbar'

export default {
  name: "app",
  components: {
    Snackbar
  },
  data: function() {
    return {
      messages: [] // 消息隊列
    }
  },
  mounted() {
    // 全局 Snackbar 控制器事件監聽
    this.snackbarController()
  },
  methods: {
    snackbarController() {
      // 監聽 showSnackbar 事件
      this.$eventBus.$on('showSnackbar', data => {
        // 將收到的message推入messages數組中
        this.messages.push({
          ...data,
          show: true
        })
      })
    },
    onSnackbarClose(value, index) {
      // value 為 Snackbar 組件內部傳遞出來的
      // index 為當前關閉 Snackbar 的 索引
      // 刪除已關閉的 Snackbar 對應的消息數據
      this.messages.splice(index, 1)
    }
  }
}
</script>

<style lang="less">
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  background-color: #212121;
  color: #cecece;
  font-size: 14px;
}
</style>

3. 調用Snackbar

這是一個模擬觸發消息的頁面;

project/src/views/snackbar.vue

<template>
  <div class="view-snackbar">
    <div class="wrap">
      <input type="text" v-model="msg" class="msg-input" placeholder="說點什么...">
      <div class="btn" @click="showMessage">顯示消息</div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'view-snackbar',
    data: function () {
      return {
        msg: ''
      }
    },
    methods: {
      showMessage() {
        // 通過觸發 showSnackbar 事件並傳遞消息參數,從而調用全局 Snackbar
        this.$eventBus.$emit('showSnackbar', {
          id: new Date().getTime(), // id 用於設置 Snackbar 在 v-for 循環中的 key 屬性,避免排序混亂的問題
          content: this.msg,
          closable: true
        })
      }
    }
  }
</script>

<style lang="less">
.view-snackbar {
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-image: url(https://img.cc0.cn/pixabay/2019102201563350617.jpg/content);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  .wrap {
    width: 240px;
  }
  .msg-input {
    width: 100%;
    height: 40px;
    color: inherit;
    background-color: #333333;
    border: 2px solid #444444;
    border-radius: 8px;
    outline: none;
    padding: 0 8px;
  }
  .btn {
    width: 120px;
    margin: 20px auto;
    padding: 8px 0;
    text-align: center;
    border-radius: 8px;
    background-color: deeppink;
    cursor: pointer;
    user-select: none;
  }
}
</style>

4. 總結

通過上述4步就實現了簡單的 Vue Snackbar 消息條隊列顯示,並且依次動畫消失,思路還是很清晰的,主要有3個要點:

  • 使用了全局控制器,並通過事件總線 $eventBus 傳遞消息;
  • 在自定義組件中使用 v-model 實現 Snackbar 的刪除;
  • 利用 Snackbar 索引動態計算 Y 方向上的位移實現 Snackbar 的有序排列;

本教程旨在描述思路,實現比較簡單,未做過多封裝和定制,希望能幫到有需要的童鞋,如發現任何問題,或有更多實現方式,歡迎一起討論!

5. 改善

可在本版基礎上做更多完善,有興趣的童鞋可以自己玩玩;

  • Snackbar 漸變出現/消失;
  • 多種高度的 Snackbar 混合使用;

本文出處:https://www.cnblogs.com/zhenfengxun/
本文鏈接:https://www.cnblogs.com/zhenfengxun/p/12452814.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM