noticebar/noticebar.vue

<template>
    <div class="vm-noticebar">
        <div class="vm-icon" v-if="typeClass" :class="typeClass"></div>
        <div class="vm-noticebar-wrap" ref="noticeBarWrap">
            <div class="vm-noticebar-content"
                 :style="styleObj"
                 :class="{'animate':shouldAnimate}"
                 ref="noticeBarContent">
                <slot></slot>
            </div>
        </div>
        <div class="vm-action" v-if="actionClass" @click="close" :class="actionClass"></div>
    </div>
</template>
<style lang="scss" src="./style.scss"></style>
<script type="text/javascript">
  /**
   * @component NoticeBar
   * @description
   *
   * ## 通知组件(小喇叭) / NoticeBar
   *
   * ### 介绍
   *
   * 这个组件是在支付宝中看到, 且自己也在别的手机端H5项目中手写过, 因此这个组件比较有代表性, 这里进队组建行了归纳.
   *
   * ### Props说明
   *
   * - 组件左侧是类型type定义, 默认是'notice'(小喇叭图标), 如果是'warn'则为感叹号图标.
   * - 右侧是动作action定义, 默认是'close', 如果定义的是'direct', 则为向右的箭头. 不能什么类型, 点击都是关闭通知组件. 关闭动作注册在onDismiss中.
   *
   *
   * ### 如何引入
   * ```
   * // 引入
   * import { NoticeBar } from 'vimo'
   * // 安装
   * Vue.component(NoticeBar.name, NoticeBar)
   * // 或者
   * export default{
   *   components: {
   *     NoticeBar
   *  }
   * }
   * ```
   *
   * ### 如果获取组件实例
   *
   * - 通过ref标记获取
   *
   * ### 关于通知文字的长度
   *
   * 如果通知文字的长度比内容显示的宽度短, 则通知文本内容不进行滚动.
   *
   * @props {Number} [speed=30] - 指定对象动画的速度, 默认为30px/s, 传入值必须大于30
   * @props {Number} [delay=2] - 指定对象动画延迟的时间
   * @props {String} [type='notice'] - 左侧的icon类型, 可以是:  notice/warn/hide
   * @props {String} [action='close'] - 左侧的icon类型, 可以是:  close/direct/hide
   * @props {Function} [onDismiss] - 组件关闭的回调
   *
   *
   * @demo #/notice-bar
   * @usage
   *
   * <NoticeBar :onDismiss="onDismissHandler">这是一个很简短的通知, 不会滚动</NoticeBar>
   * <NoticeBar class="noticeBar" action="hide">
   *    国庆节期间余额宝收益和转出到账时间调整到节后, 给您带来不便, 敬请谅解!
   * </NoticeBar>
   *
   * */
  export default {
    name: 'NoticeBar',
    data () {
      return {
        shouldAnimate: false,
        duration: 9,
        typeClass: null,
        actionClass: null
      }
    },
    props: {
      speed: {
        type: Number,
        validator (value) {
          return value >= 30
        },
        default: 30 // 100px/s
      },
      delay: {
        type: Number,
        default: 2
      },
      type: {
        type: String,
        default: 'notice' // notice/warn/hide
      },
      action: {
        type: String,
        default: 'close' // close/direct/hide
      },
      onDismiss: Function
    },
    computed: {
      styleObj () {
        return {
          'animationDuration': `${this.duration}s`,
          'animationDelay': `${this.delay}s`
        }
      },
      noticeBarWrapElement () {
        return this.$refs.noticeBarWrap
      },
      noticeBarContentElement () {
        return this.$refs.noticeBarContent
      }
    },
    methods: {
      /**
       * @function close
       * @description
       * 关闭
       * */
      close () {
        this.onDismiss && this.onDismiss()
        this.$el.remove()
      }
    },
    created () {
      switch (this.type) {
        case 'notice':
          this.typeClass = 'vm-icon-notice'
          break
        case 'warn':
          this.typeClass = 'vm-icon-warn'
          break
        case 'hide':
          this.typeClass = ''
          break
        default:
          this.typeClass = ''
      }

      switch (this.action) {
        case 'close':
          this.actionClass = 'vm-icon-close'
          break
        case 'right':
          this.actionClass = 'vm-icon-right'
          break
        case 'hide':
          this.actionClass = ''
          break
        default:
          this.actionClass = ''
      }
    },
    mounted () {
      var wrapRect = this.noticeBarWrapElement.getBoundingClientRect()
      var contentRect = this.noticeBarContentElement.getBoundingClientRect()
      if (contentRect.width > wrapRect.width) {
        this.shouldAnimate = true
      }

      // 计算持续时间
      this.duration = contentRect.width * 0.87 / this.speed >> 0
    },
    components: {}
  }
</script>