Vue3+TS實現(xiàn)數(shù)字滾動效果CountTo組件
前言
最近開發(fā)有個需求需要酷炫的文字滾動效果,發(fā)現(xiàn)vue2版本的CountTo組件不適用與Vue3,沒有輪子咋辦,那咱造一個唄。其實大多數(shù)版本更替導致公共組件不可用,最簡單的做法就是在原版本的基礎上進行修改調(diào)整,總體來講花費的時間成本以及精力成本最低。
思考

先看下效果,明確需求,然后開始搬磚。
明確基礎功能
- 有開始值、結束值以及動畫持續(xù)時間
- 默認分隔符、自動播放
擴展功能
- 自動播放可配置
- 分隔符可自定義
- 前、后綴
- 動畫配置項
實踐
定義參數(shù)
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
}
}
}定義一個開始函數(shù)
// 定義一個計算屬性,當開始數(shù)字大于結束數(shù)字時返回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掛在完成后執(zhí)行一些操作
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 | 持續(xù)時間 | Number | 0 |
| autoplay | 自動播放 | Boolean | true |
| decimals | 要顯示的小數(shù)位數(shù) | Number | 0 |
| decimal | 十進制分割 | String | , |
| separator | 分隔符 | String | , |
| prefix | 前綴 | String | '' |
| suffix | 后綴 | String | '' |
| useEasing | 使用緩和功能 | Boolean | true |
| easingFn | 緩和回調(diào) | Function | - |
注:當autoplay:true時,它將在startVal或endVal更改時自動啟動
功能
| 函數(shù)名 | 描述 |
|---|---|
| mountedCallback | 掛載以后返回回調(diào) |
| start | 開始計數(shù) |
| pause | 暫停計數(shù) |
| 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))
}
// 格式化數(shù)據(jù),返回想要展示的數(shù)據(jù)格式
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
})
// 定義一個計算屬性,當開始數(shù)字大于結束數(shù)字時返回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掛在完成后執(zhí)行一些操作
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實現(xiàn)數(shù)字滾動效果CountTo組件的文章就介紹到這了,更多相關Vue3數(shù)字滾動效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
淺談Vue3.0新版API之composition-api入坑指南
這篇文章主要介紹了Vue3.0新版API之composition-api入坑指南,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
vue2和elementUI?實現(xiàn)落日余暉登錄頁和滑塊校驗功能
這篇文章主要介紹了vue2和elementUI打造落日余暉登錄頁和滑塊校驗,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2023-06-06

