前言
今天除夕,在这里祝大家新年快乐!!!今天在这个特别的日子里我们做一个消息通知组件,好,我们开始行动起来吧!!!
项目一览
效果很简单,就是这种的小卡片似的效果。
源码
我们先开始写UI页面,可自定义消息内容以及关闭按钮的样式。
Notification.vue
<template>
<transition name="fade" @after-enter="handleAfterEnter">
<div class="notification" :style="style" v-show="visible">
<span class="notification__content">
{{content}}
</span>
<span class="notification__btn" @click="handleClose">{{btn}}</span>
</div>
</transition>
</template>
<script>
export default {
name: 'Notification',
props: {
content: {
type: String,
required: true
},
btn: {
type: String,
default: 'close'
}
}
}
</script>
<style lang="less" scoped>
.fade-enter-active, .fade-leave-active{
transition: opacity 1s;
}
.fade-enter, .fade-leave-to{
opacity: 0;
transform: translateX(100px);
}
.notification{
display: flex;
background-color: #303030;
color: rgba(255, 255, 255, 1);
align-items: center;
padding: 20px;
position: fixed;
min-width: 280px;
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.3);
flex-wrap: wrap;
transition: all 0.3s;
border-radius:10px ;
&__content{
padding: 0;
}
&__btn{
color: #ff4081;
padding-left: 24px;
margin-left: auto;
cursor: pointer;
}
}
</style>
写完基本的样式组件,我们该赋予其灵魂了。好,我们开始写最重要的逻辑。
notify.js
import Vue from "vue";
import Notification from "./Notification.vue";
const NotificationConstructor = Vue.extend(Notification);
const instances = [];
let seed = 1;
// 消除Vue实例
const removeInstance = (instance) => {
if (!instance) return;
const len = instances.length;
const index = instances.findIndex((ins) => instance.id === ins.id);
instances.splice(index, 1);
if (len <= 1) return;
const removeHeight = instance.height;
for (let i = index; i < len - 1; i++) {
instances[i].verticalOffset =
parseInt(instances[i].verticalOffset) - removeHeight - 16;
}
};
const notify = (options = {}) => {
if (Vue.prototype.$isServer) return;
// 获取vue实例
let instance = new NotificationConstructor({
propsData: options, // 这里是传进来一组props
data() {
return {
verticalOffset: 0,
timer: null,
visible: false,
height: 0,
};
},
computed: {
// 配置消息组件的位置
style() {
return {
position: "fixed",
right: "20px",
bottom: `${this.verticalOffset}px`,
};
}
},
mounted() {
this.createTimer();
this.$el.addEventListener("mouseenter", () => {
if (this.timer) {
this.clearTimer(this.timer);
}
});
this.$el.addEventListener("mouseleave", () => {
if (this.timer) {
this.clearTimer(this.timer);
}
this.createTimer();
});
},
updated() {
this.height = this.$el.offsetHeight;
},
beforeDestroy() {
this.clearTimer();
},
methods: {
// 创建计时器
createTimer() {
this.timer = setTimeout(() => {
this.visible = false;
document.body.removeChild(this.$el);
removeInstance(this);
this.$destroy();
}, options.timeout || 5000);
},
// 清除计时器
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
},
// 关闭消息弹窗
handleClose() {
this.visible = false;
document.body.removeChild(this.$el);
removeInstance(this);
this.$destroy(true);
},
// 过渡js钩子
handleAfterEnter() {
this.height = this.$el.offsetHeight;
},
},
});
const id = `notification_${seed++}`; // 动态生成唯一Id
instance.id = id;
// 生成vue中的$el
instance = instance.$mount();
// 将$el中的内容插入dom节点中去
document.body.appendChild(instance.$el);
instance.visible = true;
let verticalOffset = 0;
instances.forEach((item) => {
verticalOffset += item.$el.offsetHeight + 16;
});
verticalOffset += 16;
instance.verticalOffset = verticalOffset;
instances.push(instance);
return instance;
};
export default notify;
当消息组件组高度超过浏览器显示区域的时候,消息组件会依次按顺序消失。
在这里,我们使用了Vue.extend()
,在这里我们简单地介绍下,官网上是这样介绍的。
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在Vue.extend()
中它必须是函数
<div id="app"></div>
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#app')
@after-enter="handleAfterEnter"
,看到这很多小伙伴会有疑问,其实这是Vue过渡组件中 JavaScript 钩子。
官网的解释这样讲。
可以在 attribute 中声明 JavaScript 钩子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 进入中
// --------
beforeEnter: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
1.这些钩子函数可以结合 CSS transitions/animations
使用,也可以单独使用。2.当只用 JavaScript 过渡的时候,在 enter
和 leave
中必须使用 done
进行回调。否则,它们将被同步调用,过渡会立即完成。3.推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
详情解释可以查看官方文档
整理完UI组件与逻辑文件,下一步做得工作是将它们整合起来,通过Vue命令的方式直接使用。
index.js
import Notification from "./Notification.vue";
import notify from "./notify.js";
export default (Vue) => {
Vue.component(Notification.name, Notification);
Vue.prototype.$notify = notify;
};
下面,我们将使用它。
在main.js
中引入index.js
文件。
import Notification from "../src/components/notification/index.js";
Vue.use(Notification);
然后,你在相应的组件中这样调用它即可。
this.$notify({
content: "Hello World", // 消息内容
btn: "关闭" // 关闭按钮内容
});
结语
谢谢大家阅读,希望没有浪费大家的时间。新的一年即将来临,2021年祝大家工作顺利,平平安安。
本文转转自微信公众号前端历劫之路原创https://mp.weixin.qq.com/s/SS50RmN0bo9F3P3zOSvZiA,如有侵权,请联系删除。