Vue3+TS实现数字滚动效果CountTo组件
作者:凉城a
最近开发有个需求需要酷炫的文字滚动效果,发现vue2版本的CountTo组件不适用与Vue3,没有轮子咋办,那咱造一个呗,感兴趣的小伙伴可以跟随小编一起了解一下
前言
最近开发有个需求需要酷炫的文字滚动效果,发现vue2
版本的CountTo
组件不适用与Vue3
,没有轮子咋办,那咱造一个呗。其实大多数版本更替导致公共组件不可用,最简单的做法就是在原版本的基础上进行修改调整,总体来讲花费的时间成本以及精力成本最低。
思考
先看下效果,明确需求,然后开始搬砖。
明确基础功能
- 有开始值、结束值以及动画持续时间
- 默认分隔符、自动播放
扩展功能
- 自动播放可配置
- 分隔符可自定义
- 前、后缀
- 动画配置项
实践
定义参数
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组件库了https://github.com/kinoaa/kinoaa-components/tree/main/countTo
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组件的文章就介绍到这了,更多相关Vue3数字滚动效果内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!