做一个Vue的Toast组件
louzhedong opened this issue · comments
Miracle commented
本文来基于Vue实现一个Toast组件
要点
基于Vue.extend()
方法,我们可以得到一个Vue组件的实例,并且操作这个实例
Toast.js
import Vue from 'vue';
import ToastCompent from './Toast.vue';
const instancePool = []; // 实例池
const ToastConstructor = Vue.extend(ToastCompent);
const getAnInstance = () => {
if (instancePool.length > 0) {
const instance = instancePool.shift();
return instance;
}
return new ToastConstructor({
el: document.createElement('div')
});
}
const removeDom = (event) => {
if (event.target.parentNode) {
event.target.parentNode.removeChild(event.target);
}
}
ToastConstructor.prototype.close = function () {
this.visible = false;
this.$el.addEventListener('transitionend', removeDom);
this.closed = true;
// 关闭后将实例存入实例池
instancePool.push(this);
}
/**
*
*
* @param {*} [options={
* message
* backgroundColor: rgba(0, 0, 0, 0.7)
* position ['top', 'middle', 'bottom']
* duration 3000 -1为不关闭
* className
* }]
* options 也可以是一个字符串
*/
const Toast = (options = {}) => {
const duration = options.duration || 3000;
let instance = getAnInstance();
instance.closed = false;
clearTimeout(instance.timer);
instance.message = typeof options === 'string' ? options : options.message;
instance.backgroundColor = options.backgroundColor || 'rgba(0, 0, 0, 0.7)';
instance.position = options.position || 'top';
instance.duration = options.duration || 3000;
instance.className = options.className || '';
document.body.appendChild(instance.$el);
Vue.nextTick(() => {
instance.visible = true;
instance.$el.removeEventListener('transitionend', removeDom);
~duration && (instance.timer = setTimeout(() => {
if (instance.closed) return;
instance.close();
}, duration))
});
return instance;
}
export default Toast;
Toast.vue
<template>
<transition name="fade">
<div
class="wheat-toast"
:class="computedClass"
:style="{'background': backgroundColor}"
v-show="visible"
>
<span class="wheat-toast-message">{{message}}</span>
</div>
</transition>
</template>
<script type="text/babel">
export default {
props: ["message", "backgroundColor", "position", "duration", "className"],
data() {
return {
visible: false
};
},
computed: {
computedClass() {
let classname = this.className;
switch (this.position) {
case "top":
classname += " toast-position-top";
break;
case "middle":
classname += " toast-position-middle";
break;
case "bottom":
classname += " toast-position-bottom";
break;
default:
break;
}
return classname;
}
}
};
</script>
<style lang="less">
.wheat-toast {
position: fixed;
max-width: 80%;
border-radius: 5px;
color: #fff;
box-sizing: border-box;
text-align: center;
z-index: 1000;
padding: 8px 12px;
font-size: 16px;
.wheat-toast-message {
font-size: inherit;
}
}
.toast-position-top {
top: 50px;
left: 50%;
transform: translate(-50%, 0);
}
.toast-position-middle {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.toast-position-bottom {
bottom: 50px;
left: 50%;
transform: translate(-50%, 0);
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-to,
.fade-leave {
opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
</style>
使用方式
<template>
<div class="toast-example">
<div class="beautify-button" @click="handleShowToast">显示弹窗</div>
</div>
</template>
<script>
import Toast from "wheat-ui/dist/Toast";
export default {
methods: {
handleShowToast() {
Toast({
message: "出现错误",
position: "middle"
});
Toast("只有message");
const instance = Toast({
message: "不关闭",
duration: -1
});
setTimeout(() => {
instance.close();
}, 4000);
}
}
};
</script>
```