Vue3+TS實(shí)現(xiàn)數(shù)字滾動(dòng)效果CountTo組件
前言
最近開(kāi)發(fā)有個(gè)需求需要酷炫的文字滾動(dòng)效果,發(fā)現(xiàn)vue2
版本的CountTo
組件不適用與Vue3
,沒(méi)有輪子咋辦,那咱造一個(gè)唄。其實(shí)大多數(shù)版本更替導(dǎo)致公共組件不可用,最簡(jiǎn)單的做法就是在原版本的基礎(chǔ)上進(jìn)行修改調(diào)整,總體來(lái)講花費(fèi)的時(shí)間成本以及精力成本最低。
思考
先看下效果,明確需求,然后開(kāi)始搬磚。
明確基礎(chǔ)功能
- 有開(kāi)始值、結(jié)束值以及動(dòng)畫持續(xù)時(shí)間
- 默認(rèn)分隔符、自動(dòng)播放
擴(kuò)展功能
- 自動(dòng)播放可配置
- 分隔符可自定義
- 前、后綴
- 動(dòng)畫配置項(xiàng)
實(shí)踐
定義參數(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 } } }
定義一個(gè)開(kāi)始函數(shù)
// 定義一個(gè)計(jì)算屬性,當(dāng)開(kāi)始數(shù)字大于結(jié)束數(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') }) // 組件銷毀時(shí)取消動(dòng)畫 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') } }
配置項(xiàng)
屬性 | 描述 | 類型 | 默認(rèn)值 |
---|---|---|---|
startVal | 開(kāi)始值 | Number | 0 |
endVal | 結(jié)束值 | Number | 0 |
duration | 持續(xù)時(shí)間 | Number | 0 |
autoplay | 自動(dòng)播放 | Boolean | true |
decimals | 要顯示的小數(shù)位數(shù) | Number | 0 |
decimal | 十進(jìn)制分割 | String | , |
separator | 分隔符 | String | , |
prefix | 前綴 | String | '' |
suffix | 后綴 | String | '' |
useEasing | 使用緩和功能 | Boolean | true |
easingFn | 緩和回調(diào) | Function | - |
注:當(dāng)autoplay:true時(shí),它將在startVal或endVal更改時(shí)自動(dòng)啟動(dòng)
功能
函數(shù)名 | 描述 |
---|---|
mountedCallback | 掛載以后返回回調(diào) |
start | 開(kāi)始計(jì)數(shù) |
pause | 暫停計(jì)數(shù) |
reset | 重置countTo |
組件
組件同步在git組件庫(kù)了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 }) // 定義一個(gè)計(jì)算屬性,當(dāng)開(kāi)始數(shù)字大于結(jié)束數(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') } } // 組件銷毀時(shí)取消動(dòng)畫 onUnmounted(() => { cancelAnimationFrame(state.rAF) }) return () => ( state.displayValue ) } })
到此這篇關(guān)于Vue3+TS實(shí)現(xiàn)數(shù)字滾動(dòng)效果CountTo組件的文章就介紹到這了,更多相關(guān)Vue3數(shù)字滾動(dòng)效果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue 項(xiàng)目打包時(shí)樣式及背景圖片路徑找不到的解決方式
今天小編就為大家分享一篇vue 項(xiàng)目打包時(shí)樣式及背景圖片路徑找不到的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11基于Vue.js+Nuxt開(kāi)發(fā)自定義彈出層組件
這篇文章主要介紹了基于Vue.js+Nuxt開(kāi)發(fā)自定義彈出層組件,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10淺談Vue3.0新版API之composition-api入坑指南
這篇文章主要介紹了Vue3.0新版API之composition-api入坑指南,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04iView-admin 動(dòng)態(tài)路由問(wèn)題的解決方法
這篇文章主要介紹了iView-admin 動(dòng)態(tài)路由問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10vue2和elementUI?實(shí)現(xiàn)落日余暉登錄頁(yè)和滑塊校驗(yàn)功能
這篇文章主要介紹了vue2和elementUI打造落日余暉登錄頁(yè)和滑塊校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06vue結(jié)合echarts繪制一個(gè)支持切換的折線圖實(shí)例
這篇文章主要介紹了vue結(jié)合echarts繪制一個(gè)支持切換的折線圖實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10vue移動(dòng)端使用canvas簽名的實(shí)現(xiàn)
這篇文章主要介紹了vue移動(dòng)端使用canvas簽名的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例
這篇文章主要為大家介紹了Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07