做一个纯js实现的Message

485 阅读2分钟

闲来无事,打发摸鱼时间,就想着做一个Message组件玩玩,目前就实现了以下功能点:

  1. 支持配置消失时间
  2. 支持success、info、warning、error 4种提示信息
  3. 其它都不需要了,做着玩的

下面就是代码实现,也不多,就100来行:

class Message {
  constructor(config = {}) {
    this.msgGobalContainer = null
    this.init(config)
  }

  init(config) {
    this.config = { ...this.defaultConfig(), ...config }
  }

  defaultConfig() {
    return {
      duration: 2000
    }
  }

  createMsgGobalContainer() {
    // 创建全局容器
    const msgGobalContainer = document.createElement('div')
    msgGobalContainer.classList.add('xyz-message')
    this.msgGobalContainer = msgGobalContainer
  }

  createMsgContainer() {
    const msgContainer = document.createElement('div')
    msgContainer.classList.add('xyz-message-notice')
    return msgContainer
  }

  createMsgWrap() {
    const msgWrap = document.createElement('div')
    msgWrap.classList.add('xyz-message-notice-content')
    return msgWrap
  }

  createMsgText(msg) {
    const msgText = document.createElement('span')
    msgText.innerText = msg
    return msgText
  }

  createMsgIcon(status = 'info') {
    // 创建msg的icon标时
    const icon = document.createElement('div')
    icon.classList.add('xyz-message-icon', `xyz-message-icon-${status}`)
    return icon
  }

  msgCombination(config) {
    // 组装msg容器
    const msgWrap = this.createMsgWrap()
    const msgIcon = this.createMsgIcon(config.status)
    const msgText = this.createMsgText(config.message)
    const msgContainer = this.createMsgContainer()
    msgWrap.appendChild(msgIcon)
    msgWrap.appendChild(msgText)
    msgContainer.appendChild(msgWrap)
    return msgContainer
  }

  appendMsgToBody(msgContainer) {
    if (!this.msgGobalContainer) {
      this.createMsgGobalContainer()
      document.body.appendChild(this.msgGobalContainer)
    }
    this.msgGobalContainer.appendChild(msgContainer)
    this.delMsgContainer(msgContainer)
  }

  delMsgContainer(msgContainer) {
    setTimeout(() => {
      animation(msgContainer).leave(() => msgContainer.remove())
    }, this.config.duration)
  }

  open(message, status) {
    const msgContainer = this.msgCombination({
      message,
      status
    })
    animation(msgContainer).enter()
    this.appendMsgToBody(msgContainer)
  }

  success(message) {
    this.open(message, 'success')
  }

  info(message) {
    this.open(message, 'info')
  }

  warning(message) {
    this.open(message, 'warning')
  }

  error(message) {
    this.open(message, 'error')
  }

}

function animation(el) {

  const name = 'xyz'

  const transitionClass = {
    enterClass: `${name}-enter`,
    enterToClass: `${name}-enter-to`,
    enterActiveClass: `${name}-enter-active`,
    leaveClass: `${name}-leave`,
    leaveToClass: `${name}-leave-to`,
    leaveActiveClass: `${name}-leave-active`
  }

  const enter = (cb) => {
    // 执行进入动画
    el.classList.add(transitionClass.enterClass, transitionClass.enterToClass)
    requestAnimationFrame(() => {
      el.classList.add(transitionClass.enterActiveClass)
    })
    const transitionend = () => {
      el.classList.remove(transitionClass.enterClass, transitionClass.enterToClass, transitionClass.enterActiveClass)
      el.removeEventListener('transitionend', transitionend)
      if (cb && typeof cb === 'function') cb() // 动画执行完的回调函数
    }
    el.addEventListener('transitionend', transitionend)
  }

  const leave = (cb) => {
    // 执行离开动画
    el.classList.add(transitionClass.leaveClass, transitionClass.leaveToClass)
    requestAnimationFrame(() => {
      el.classList.add(transitionClass.leaveActiveClass)
    })
    const transitionend = () => {
      el.classList.remove(transitionClass.leaveClass, transitionClass.leaveToClass, transitionClass.leaveActiveClass)
      el.removeEventListener('transitionend', transitionend)
      if (cb && typeof cb === 'function') cb() // 动画执行完的回调函数
    }
    el.addEventListener('transitionend', transitionend)
  }

  return {
    enter,
    leave
  }

}

这里动画部分的代码占了1/3,不然大概100行就完事了。接下来就是样式代码,也不多,写着玩的:

.xyz-message {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  color: #000000d9;
  font-size: 14px;
  line-height: 22px;
  list-style: none;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1010;
  width: 100%;
  pointer-events: none;
}

.xyz-message-notice {
  text-align: center;
  margin-top: 20px;
}

.xyz-message-notice-content {
  display: inline-block;
  padding: 10px 20px;
  background-color: #fff;
  box-shadow: 0 2px 8px 1px #ddd;
}

.xyz-message-icon {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 10px;
  border-radius: 50%;
  background-color: #0d71fcd9;
}

.xyz-message-icon-success {
  background-color: #29fc0dd9;
}

.xyz-message-icon-info {
  background-color: #0d71fcd9;
}

.xyz-message-icon-warning {
  background-color: #fc890dd9;
}

.xyz-message-icon-error {
  background-color: #eb1d07d9;
}

.xyz-enter {
  opacity: 0;
  transform: translateY(-20px);
}

.xyz-enter-to, .xyz-leave-to {
  transition: all .3s ease;
}

.xyz-enter-active, .xyz-leave {
  opacity: 1;
  transform: translateY(0);
}

.xyz-leave-active {
  opacity: 0;
  height: 20px;
  transform: translateY(-20px);
}

样式部分就很简单了,分2部分,1部分是定义Message容器位置与各种状态下的样式。另一部分就是定义下动画

最后就是使用它了,那就简单了,初始化下Message实例,放个按钮直接调用就完事了:

const message = new Message({
  duration: 2000
})
const submitBtn = document.getElementById('submit')
submitBtn.addEventListener('click', (e) => {
  message.success('提交成功')
})

这里就写了Message组件的简单实现,要求不高也能用,其实可以再在Message类上扩展其它方法、配置项属性。比如可以自定义icon、自定义内容区、loading异步等等。收工,下次摸鱼再写个弹框的。