<template>
  <view
    class="tn-count-num-class tn-count-num"
    :class="[fontColorClass]"
    :style="{
      fontSize: fontSizeStyle || '50rpx',
      fontWeight: bold ? 'bold' : 'normal',
      color: fontColorStyle || '#080808'
    }"
  >
    {{ displayValue }}
  </view>
</template>

<script>
  import componentsColorMixin from '../../libs/mixin/components_color.js'
  export default {
    name: 'tn-count-to',
    mixins: [componentsColorMixin],
    props: {
      // 开始的数值,默认为0
      startVal: {
        type: Number,
        default: 0
      },
      // 结束目标数值
      endVal: {
        type: Number,
        default: 0,
        required: true
      },
      // 是否自动开始
      autoplay: {
        type: Boolean,
        default: true
      },
      // 滚动到目标值的持续时间,单位为毫秒
      duration: {
        type: Number,
        default: 2000
      },
      // 是否在即将结束的时候使用缓慢滚动的效果
      useEasing: {
        type: Boolean,
        default: true
      },
      // 显示的小数位数
      decimals: {
        type: Number,
        default: 0
      },
      // 十进制的分割符
      decimalSeparator: {
        type: String,
        default: '.'
      },
      // 千分位的分隔符
      // 类似金额的分割(¥23,321.05中的",")
      thousandthsSeparator: {
        type: String,
        default: ''
      },
      // 是否显示加粗字体
      bold: {
        type: Boolean,
        default: false
      }
    },
    computed: {
      countDown() {
        return this.startVal > this.endVal
      }
    },
    data() {
      return {
        localStartVal: this.startVal,
        localDuration: this.duration,
        // 显示的数值
        displayValue: this.formatNumber(this.startVal),
        // 打印的数值
        printValue: null,
        // 是否暂停
        paused: false,
        // 开始时间戳
        startTime: null,
        // 停留时间戳
        remainingTime: null,
        // 当前时间戳
        timestamp: null,
        // 上一次的时间戳
        lastTime: 0,
        rAF: null
      }
    },
    watch: {
      startVal() {
        this.autoplay && this.start()
      },
      endVal() {
        this.autoplay && this.start()
      }
    },
    mounted() {
      this.autoplay && this.start()
    },
    methods: {
      // 开始滚动
      start() {
        this.localStartVal = this.startVal
        this.startTime = null
        this.localDuration = this.duration
        this.paused = false
        this.rAF = this.requestAnimationFrame(this.count)
      },
      // 重新开始
      reStart() {
        if (this.paused) {
          this.resume()
          this.paused = false
        } else {
          this.stop()
          this.paused = true
        }
      },
      // 停止
      stop() {
        this.cancelAnimationFrame(this.rAF)
      },
      // 恢复
      resume() {
        this.startTime = null
        this.localDuration = this.remainingTime
        this.localStartVal = this.printValue
        this.requestAnimationFrame(this.count)
      },
      // 重置
      reset() {
        this.startTime = null
        this.cnacelAnimationFrame(this.rAF)
        this.displayValue = this.formatNumber(this.startVal)
      },
      // 销毁组件
      destroyed() {
        this.cancelAnimationFrame(this.rAF)
      },
      // 累加时间
      count(timestamp) {
        if (!this.startTime) this.startTime = timestamp
        this.timestamp = timestamp
        const progress = timestamp - this.startTime
        this.remainingTime = this.localDuration - progress
        if (this.useEasing) {
          if (this.countDown) {
            this.printValue = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration)
          } {
            this.printValue = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration)
          }
        } else {
          if (this.countDown) {
            this.printValue = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration)
          } else {
            this.printValue = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration)
          }
        }
        if (this.countDown) {
          this.printValue = this.printValue < this.endVal ? this.endVal : this.printValue
        } else {
          this.printValue = this.printValue > this.endVal ? this.endVal : this.printValue
        }
        
        this.displayValue = this.formatNumber(this.printValue)
        if (progress < this.localDuration) {
          this.rAF = this.requestAnimationFrame(this.count)
        } else {
          this.$emit('end')
        }
      },
      // 缓动时间计算
      easingFn(t, b, c, d) {
        return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
      },
      // 请求帧动画
      requestAnimationFrame(cb) {
        const currentTime = new Date().getTime()
        // 为了使setTimteout的尽可能的接近每秒60帧的效果
        const timeToCall = Math.max(0, 16 - (currentTime - this.lastTime))
        const timerId = setTimeout(() => {
          cb && cb(currentTime + timeToCall)
        }, timeToCall)
        this.lastTime = currentTime + timeToCall
        return timerId
      },
      // 清除帧动画
      clearAnimationFrame(timerId) {
        clearTimeout(timerId)
      },
      // 格式化数值
      formatNumber(number) {
        const reg = /(\d+)(\d{3})/
        number = Number(number)
        number = number.toFixed(Number(this.decimals))
        number += ''
        const numberArray = number.split('.')
        let num1 = numberArray[0]
        const num2 = numberArray.length > 1 ? this.decimalSeparator + numberArray[1] : ''
        
        if (this.thousandthsSeparator && !this.isNumber(this.thousandthsSeparator)) {
          while(reg.test(num1)) {
            num1 = num1.replace(reg, '$1' + this.thousandthsSeparator + '$2')
          }
        }
        return num1 + num2
      },
      // 判断是否为数字
      isNumber(val) {
        return !isNaN(parseFloat(val))
      }
    }
  }
</script>

<style lang="scss" scoped>
  
  .tn-count-num {
    /* #ifndef APP-NVUE */
    display: inline-flex;
    /* #endif */
    text-align: center;
    line-height: 1;
  }
</style>