本文正在参与「金石计划 . 切割6万现金大奖」
前言
最近开发有个需求需要酷炫的文字翻滚作用,发现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组件库了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
)
}
})
往期精彩回顾
学会这些鲜有人知的coding技巧,从此早早下班liao-JavaScript实战技巧篇
前端图片最优化紧缩计划
Vue实战技巧Element Table二次封装
写在最后
我是凉城a,一个前端,热爱技能也热爱生活。
与你相逢,我很高兴。
假如你想了解更多,请点这里,等待你的小⭐⭐
- 文中如有错误,欢迎在评论区纠正,假如这篇文章帮到了你,欢迎点赞和重视
- 本文首发于,未经许可禁止转载