Vue实现notification组件

实现效果

要实现fixed定位的元素也要呈现有次序和有动画的出现和消失


如何实现

我们都知道上面的元素不过就是一个个实例的加入到页面中和到时间就移除,那么要怎么做到呢?

首先准备好元素的notification.vue组件文件,样式和动画我直接搬运ElementUI的,position默认右上角。

<template>
 <transition name="el-notification-fade">
    <div 
    :class="['el-notification',horizontalClass]" 
    v-show="visible"
    :style="positionstyle">
    <div class="el-notification__group">
    <h2 class="el-notification__title"></h2>
    <div class="el-notification__content"></div>
    <div :class="['el-notification__closeBtn',{showClose:'el-icon-close'}]"></div>
      {{title}}
  </div>
 </div>
 </transition>
</template>

当然,我们需要一个中心去管理所有实例,创建main.js,引入我们的创建的组件。

import Vue from 'vue'
import notification from './main.vue'

Vue提供给我们Vue.extend方法来创建一个子类

const  notificationconstructor=Vue.extend(notification)

用数组来存储我们的实例const instances=[]

因为最后我们是把main.js挂载到Vue的原型链上,作为全局方法调用。

Vue.prototype.$fannotify=notification

所以我们每次调用这个方法就需要去管理我们的实例

调用

  this.$fannotify({
              title:'fanfan',
              duration:4000
  })
const notify=((options)=>{
    ...见下
})

export default notify

对于传入参数进行处理赋值。

通过new创建实例来传入方法和参数(简洁版)

 let instance=new notificationconstructor({
        propsData:options,
        data() {
            return {
                timer:null,
                visible:false
            }
        },
        mounted(){
           this.hanlestart()
       
        },

        beforeDestroy(){
            this.cleartime()
        },

        methods:{
            hanlestart(){
                this.timer=setTimeout(()=>{
                    this.handleclose()
                },duration)
            },
            handleclose(){
                this.visible=false
                /**要记得先获取高度 再remove */
                notify.removeInstance(instance)
                document.body.removeChild(this.$el)
            },

            cleartime(){
                clearTimeout(this.timer)
            }
        },
    })

给实例传入各种方法和参数后就需要挂载到body上面了。

通过Vue.$mount来手动挂载实例。

如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例

如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。

所以我们用原生DOM方式插入这个实例

  4instance.id=id++
  instance = instance.$mount()
  document.body.appendChild(instance.$el)
  instance.visible=true

这样这个元素就能正常的插入Body里面且能正常的消失了

但并没有高度差和消失的一个个消失的动画效果,那么要如何实现统一管理呢?


实例统一管理

对于每个元素的生成和消失,我们都应该触发对应的方法来改变这个元素的top位置,

对于每个进来的元素

 //高度差 暂时不考虑自己传个offset进来
    let verticalOffset =0;

    instances.forEach((item)=>{
        verticalOffset+=item.$el.offsetHeight+16
    })
    verticalOffset += 16;
    instance.verticalOffset=verticalOffset

    instances.push(instance)

计算好这个新元素应该具有的高度再push进数组中

而对应每个remove的元素我们调用对应的remove方法

/**每个实例的消失 其余的top都要改变 */
notify.removeInstance=function(instance){
    //要从数组移除这个元素 且修改数组所有元素的top值 看起来就像被挤掉一样
    let len=instances.length

    let index=instances.findIndex(item=>item.id==instance.id)
    console.log(index);

    /**获取实例的高度 */
    let instanceHeight=instance.$el.offsetHeight
    console.log(instanceHeight);
    instances.splice(index,1)

    if(len<1) return 
    for (let i = index; i < len - 1; i++) {
        instances[i].verticalOffset = parseInt(instances[i].verticalOffset) - instanceHeight - 16
    }

}

这样产生的距离感就让我们看起来很有顺序一样的出现和消失。