本文正在参与「金石计划 . 切割6万现金大奖」

前言

  最近开发有个需求需要酷炫的文字翻滚作用,发现vue2版别的CountTo组件不适用与Vue3,没有轮子咋办,那咱造一个呗。其实大多数版别替换导致公共组件不可用,最简单的做法就是在原版别的根底上进行修改调整,整体来讲花费的时刻成本以及精力成本最低。

考虑

Vue3+TS写个数字翻滚作用CountTo组件

  先看下作用,清晰需求,然后开端搬砖。

清晰根底功用

  • 有开端值、完毕值以及动画持续时刻
  • 默认分隔符、自动播放

扩展功用

  • 自动播放可装备
  • 分隔符可自界说
  • 前、后缀
  • 动画装备项

实践

界说参数

const props = {
  start: {
    type: Number,
    required: false,
    default: 0
  },
  end: {
    type: Number,
    required: false,
    default: 0
  },
  duration: {
    type: Number,
    required: false,
    default: 5000
  },
  autoPlay: {
    type: Boolean,
    required: false,
    default: true
  },
  decimals: {
    type: Number,
    required: false,
    default: 0,
    validator(value) {
      return value >= 0
    }
  },
  decimal: {
    type: String,
    required: false,
    default: '.'
  },
  separator: {
    type: String,
    required: false,
    default: ','
  },
  prefix: {
    type: String,
    required: false,
    default: ''
  },
  suffix: {
    type: String,
    required: false,
    default: ''
  },
  useEasing: {
    type: Boolean,
    required: false,
    default: true
  },
  easingFn: {
    type: Function,
    default(t, b, c, d) {
      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
    }
  }
}

界说一个开端函数

    // 界说一个核算属性,当开端数字大于完毕数字时回来true
    const stopCount = computed(() => {
      return props.start > props.end
    })
    const startCount = () => {
      state.localStart = props.start
      state.startTime = null
      state.localDuration = props.duration
      state.paused = false
      state.rAF = requestAnimationFrame(count)
    }
    watch(() => props.start, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    watch(() => props.end, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    // dom挂在完成后履行一些操作
    onMounted(() => {
      if (props.autoPlay) {
        startCount()
      }
      emit('onMountedcallback')
    })
     // 组件毁掉时撤销动画
    onUnmounted(() => {
      cancelAnimationFrame(state.rAF)
    })

核心办法

    const count = (timestamp) => {
      if (!state.startTime) state.startTime = timestamp
      state.timestamp = timestamp
      const progress = timestamp - state.startTime
      state.remaining = state.localDuration - progress
      // 是否运用速度变化曲线
      if (props.useEasing) {
        if (stopCount.value) {
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)
        } else {
          state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
        }
      } else {
        if (stopCount.value) {
          state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))
        } else {
          state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)
        }
      }
      if (stopCount.value) {
        state.printVal = state.printVal < props.end ? props.end : state.printVal
      } else {
        state.printVal = state.printVal > props.end ? props.end : state.printVal
      }
      state.displayValue = formatNumber(state.printVal)
      if (progress < state.localDuration) {
        state.rAF = requestAnimationFrame(count)
      } else {
        emit('callback')
      }
    }

装备项

属性 描绘 类型 默认值
startVal 开端值 Number 0
endVal 完毕值 Number 0
duration 持续时刻 Number 0
autoplay 自动播放 Boolean true
decimals 要显现的小数位数 Number 0
decimal 十进制切割 String ,
separator 分隔符 String ,
prefix 前缀 String
suffix 后缀 String
useEasing 运用平缓功用 Boolean true
easingFn 平缓回调 Function

注:当autoplay:true时,它将在startVal或endVal更改时自动启动

功用

函数名 描绘
mountedCallback 挂载以后回来回调
start 开端计数
pause 暂停计数
reset 重置countTo

组件

组件同步在git组件库了github.com/kinoaa/kino…

import {
  defineComponent, reactive, computed, onMounted, watch, onUnmounted
} from 'vue'
const props = {
  start: {
    type: Number,
    required: false,
    default: 0
  },
  end: {
    type: Number,
    required: false,
    default: 2022
  },
  duration: {
    type: Number,
    required: false,
    default: 5000
  },
  autoPlay: {
    type: Boolean,
    required: false,
    default: true
  },
  decimals: {
    type: Number,
    required: false,
    default: 0,
    validator(value) {
      return value >= 0
    }
  },
  decimal: {
    type: String,
    required: false,
    default: '.'
  },
  separator: {
    type: String,
    required: false,
    default: ','
  },
  prefix: {
    type: String,
    required: false,
    default: ''
  },
  suffix: {
    type: String,
    required: false,
    default: ''
  },
  useEasing: {
    type: Boolean,
    required: false,
    default: true
  },
  easingFn: {
    type: Function,
    default(t, b, c, d) {
      return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
    }
  }
}
export default defineComponent({
  name: 'CountTo',
  props: props,
  emits: ['onMountedcallback', 'callback'],
  setup(props, {emit}) {
    const isNumber = (val) => {
      return !isNaN(parseFloat(val))
    }
    // 格式化数据,回来想要展示的数据格式
    const formatNumber = (val) => {
      val = val.toFixed(props.start)
      val += ''
      const x = val.split('.')
      let x1 = x[0]
      const x2 = x.length > 1 ? props.decimal + x[1] : ''
      const rgx = /(d+)(d{3})/
      if (props.separator && !isNumber(props.separator)) {
        while (rgx.test(x1)) {
          x1 = x1.replace(rgx, '$1' + props.separator + '$2')
        }
      }
      return props.prefix + x1 + x2 + props.suffix
    }
    const state = reactive<{
      localStart: number
      displayValue: number|string
      printVal: any
      paused: boolean
      localDuration: any
      startTime: any
      timestamp: any
      remaining: any
      rAF: any
    }>({
      localStart: props.start,
      displayValue: formatNumber(props.start),
      printVal: null,
      paused: false,
      localDuration: props.duration,
      startTime: null,
      timestamp: null,
      remaining: null,
      rAF: null
    })
    // 界说一个核算属性,当开端数字大于完毕数字时回来true
    const stopCount = computed(() => {
      return props.start > props.end
    })
    const startCount = () => {
      state.localStart = props.start
      state.startTime = null
      state.localDuration = props.duration
      state.paused = false
      state.rAF = requestAnimationFrame(count)
    }
    watch(() => props.start, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    watch(() => props.end, () => {
      if (props.autoPlay) {
        startCount()
      }
    })
    // dom挂在完成后履行一些操作
    onMounted(() => {
      if (props.autoPlay) {
        startCount()
      }
      emit('onMountedcallback')
    })
    const count = (timestamp) => {
      if (!state.startTime) state.startTime = timestamp
      state.timestamp = timestamp
      const progress = timestamp - state.startTime
      state.remaining = state.localDuration - progress
      // 是否运用速度变化曲线
      if (props.useEasing) {
        if (stopCount.value) {
          state.printVal = state.localStart - props.easingFn(progress, 0, state.localStart - props.end, state.localDuration)
        } else {
          state.printVal = props.easingFn(progress, state.localStart, props.end - state.localStart, state.localDuration)
        }
      } else {
        if (stopCount.value) {
          state.printVal = state.localStart - ((state.localStart - props.end) * (progress / state.localDuration))
        } else {
          state.printVal = state.localStart + (props.end - state.localStart) * (progress / state.localDuration)
        }
      }
      if (stopCount.value) {
        state.printVal = state.printVal < props.end ? props.end : state.printVal
      } else {
        state.printVal = state.printVal > props.end ? props.end : state.printVal
      }
      state.displayValue = formatNumber(state.printVal)
      if (progress < state.localDuration) {
        state.rAF = requestAnimationFrame(count)
      } else {
        emit('callback')
      }
    }
    // 组件毁掉时撤销动画
    onUnmounted(() => {
      cancelAnimationFrame(state.rAF)
    })
    return () => (
      state.displayValue
    )
  }
})

往期精彩回顾

Vue3+TS写个数字翻滚作用CountTo组件

学会这些鲜有人知的coding技巧,从此早早下班liao-JavaScript实战技巧篇

Vue3+TS写个数字翻滚作用CountTo组件
前端图片最优化紧缩计划

Vue3+TS写个数字翻滚作用CountTo组件
Vue实战技巧Element Table二次封装

写在最后

我是凉城a,一个前端,热爱技能也热爱生活。

与你相逢,我很高兴。

假如你想了解更多,请点这里,等待你的小⭐⭐

  • 文中如有错误,欢迎在评论区纠正,假如这篇文章帮到了你,欢迎点赞和重视
  • 本文首发于,未经许可禁止转载