效果預覽
思路
- 封裝 Snackbar 組件;
- 在根路由頁面下建立全局 Snackbar 控制器,統一管理 Snackbar;
- 通過事件通知全局 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