Skip to content

Commit f99b0db

Browse files
committed
fix: 动态数字组件bug,不能重复计算step
1 parent 4c30e17 commit f99b0db

File tree

4 files changed

+155
-18
lines changed

4 files changed

+155
-18
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2022 toimc <brian@toimc.com>
3+
Copyright (c) 2022 toimc <admin@wayearn.com>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

src/components/DNumbers/CountTo.vue

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<template>
2+
<span :style="{ color }">
3+
{{ value }}
4+
</span>
5+
</template>
6+
<script lang="ts">
7+
import { TransitionPresets } from '@vueuse/core'
8+
import { isNumber } from 'lodash'
9+
10+
const props = {
11+
startVal: { type: Number, default: 0 },
12+
endVal: { type: Number, default: 2021 },
13+
duration: { type: Number, default: 1500 },
14+
autoplay: { type: Boolean, default: true },
15+
decimals: {
16+
type: Number,
17+
default: 0,
18+
validator(value: number) {
19+
return value >= 0
20+
}
21+
},
22+
prefix: { type: String, default: '' },
23+
suffix: { type: String, default: '' },
24+
separator: { type: String, default: ',' },
25+
decimal: { type: String, default: '.' },
26+
/**
27+
* font color
28+
*/
29+
color: { type: String },
30+
/**
31+
* Turn on digital animation
32+
*/
33+
useEasing: { type: Boolean, default: true },
34+
/**
35+
* Digital animation
36+
*/
37+
transition: { type: String, default: 'linear' }
38+
}
39+
40+
export default defineComponent({
41+
name: 'CountTo',
42+
props,
43+
emits: ['onStarted', 'onFinished'],
44+
setup(props, { emit }) {
45+
const source = ref(props.startVal)
46+
const disabled = ref(false)
47+
let outputValue = useTransition(source)
48+
49+
const value = computed(() => formatNumber(unref(outputValue)))
50+
51+
watchEffect(() => {
52+
source.value = props.startVal
53+
})
54+
55+
watch([() => props.startVal, () => props.endVal], () => {
56+
if (props.autoplay) {
57+
start()
58+
}
59+
})
60+
61+
onMounted(() => {
62+
props.autoplay && start()
63+
})
64+
65+
function start() {
66+
run()
67+
source.value = props.endVal
68+
}
69+
70+
function reset() {
71+
source.value = props.startVal
72+
run()
73+
}
74+
75+
function run() {
76+
outputValue = useTransition(source, {
77+
disabled,
78+
duration: props.duration,
79+
onFinished: () => emit('onFinished'),
80+
onStarted: () => emit('onStarted'),
81+
...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {})
82+
})
83+
}
84+
85+
function formatNumber(num: number | string) {
86+
if (!num && num !== 0) {
87+
return ''
88+
}
89+
const { decimals, decimal, separator, suffix, prefix } = props
90+
num = Number(num).toFixed(decimals)
91+
num += ''
92+
93+
const x = num.split('.')
94+
let x1 = x[0]
95+
const x2 = x.length > 1 ? decimal + x[1] : ''
96+
97+
const rgx = /(\d+)(\d{3})/
98+
if (separator && !isNumber(separator)) {
99+
while (rgx.test(x1)) {
100+
x1 = x1.replace(rgx, '$1' + separator + '$2')
101+
}
102+
}
103+
return prefix + x1 + x2 + suffix
104+
}
105+
106+
return { value, start, reset }
107+
}
108+
})
109+
</script>

src/components/DNumbers/index.vue renamed to src/components/DNumbers/DNumbers.vue

+44-17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import { CSSProperties, defineComponent } from 'vue'
1515
import bigInt from 'big-integer'
1616
import { rand } from '@/utils'
17+
import { isFunction, isNumber } from 'lodash'
1718
1819
export default defineComponent({
1920
props: {
@@ -58,23 +59,23 @@
5859
style: {
5960
type: Object as PropType<CSSProperties>,
6061
default: () => ({})
61-
}
62+
},
6263
// todo
64+
split: {
65+
type: String,
66+
default: ''
67+
},
6368
// 比如,可以由,或者.来分隔数据
64-
// split: {
65-
// type: String,
66-
// default: ''
67-
// },
6869
// 比如,可以设置单位,千,百,万,亿,兆
6970
// unit: {
7071
// type: String as PropType<>,
7172
// default: '',
7273
// }
7374
// 比如,用户可以定义format格式
74-
// format: {
75-
// type: String as PropType<>,
76-
// default: '',
77-
// }
75+
format: {
76+
type: Function,
77+
default: null
78+
}
7879
},
7980
setup(props, { expose }) {
8081
const { setupDuration } = unref(props)
@@ -87,6 +88,7 @@
8788
8889
let ctrl
8990
91+
// 先把带小数的数放大,倍数是:10的倍数 的 小数位数 次方,比如:1.12345,那么放大后是:1.12345 * 10^5
9092
function getTimesNum(number, times) {
9193
const str = number.toString()
9294
if (str.indexOf('.') !== -1) {
@@ -102,23 +104,44 @@
102104
}
103105
}
104106
107+
// 加分隔符
108+
function formatWithSeperator(val: string, separator: string) {
109+
const num = val.split('.')
110+
let x1 = num[0]
111+
const x2 = num.length > 1 ? '.' + num[1] : ''
112+
const rgx = /(\d+)(\d{3})/
113+
if (separator && !isNumber(separator)) {
114+
while (rgx.test(x1)) {
115+
x1 = x1.replace(rgx, '$1' + separator + '$2')
116+
}
117+
}
118+
return x1 + x2
119+
}
120+
121+
// 格式化
105122
function format(number, digits) {
123+
// 用户传递了format方法,直接返回
124+
if (isFunction(props.format)) {
125+
return formatWithSeperator(props.format(number), props.split)
126+
}
106127
const str = number.toString()
128+
let tmp
107129
if (digits > 0) {
108130
if (str.indexOf('.') === -1) {
109131
// number中无"."
110-
return (str + '.').padEnd(digits + str.length + 1, '0')
132+
tmp = (str + '.').padEnd(digits + str.length + 1, '0')
111133
} else {
112134
// number中无中有".""
113135
if (str.split('.')[1].length > digits) {
114-
return str.split('.')[0] + '.' + str.split('.')[1].substr(0, digits)
136+
tmp = str.split('.')[0] + '.' + str.split('.')[1].substr(0, digits)
115137
} else {
116-
return str.split('.')[0] + '.' + str.split('.')[1].padEnd(digits, '0')
138+
tmp = str.split('.')[0] + '.' + str.split('.')[1].padEnd(digits, '0')
117139
}
118140
}
119141
} else {
120-
return str.substr(0, str.length - digits)
142+
tmp = str.substr(0, str.length - digits)
121143
}
144+
return formatWithSeperator(tmp, props.split)
122145
}
123146
124147
function init() {
@@ -145,15 +168,17 @@
145168
times = bigInt(10).pow(dot.value)
146169
}
147170
171+
// 初始值与结束值
148172
origin = getTimesNum(begin.value, times)
149173
target = getTimesNum(end.value, times)
150174
151175
// 计算定时器的倍率
152176
const rate = bigInt(duration.value).divide(setupDuration)
153177
178+
// 计算step, 并加入随机性,这样就不会有很多0
179+
let step = rate.compareAbs(bigInt.zero) !== 0 ? target.minus(origin).divide(rate) : target
180+
154181
ctrl = setInterval(() => {
155-
// 计算step, 并加入随机性,这样就不会有很多0
156-
let step = rate.compareAbs(bigInt.zero) !== 0 ? target.minus(origin).divide(rate) : target
157182
if (step.compareAbs(bigInt.zero) === 1) {
158183
const len = step.toString().length
159184
step = bigInt(rand(len))
@@ -167,7 +192,7 @@
167192
// format
168193
let divide = origin.divmod(times)
169194
170-
result.value =
195+
let tmp =
171196
divide.quotient.toString() +
172197
(dot.value > 0 ? '.' : '') +
173198
(divide.remainder.toString() !== '0'
@@ -176,7 +201,9 @@
176201
: divide.remainder.toString()
177202
: '')
178203
179-
// 如果达到目标值,则停止
204+
tmp = isFunction(props.format) ? props.format(tmp) : tmp
205+
result.value = formatWithSeperator(tmp, props.split)
206+
// 如果达到目标值cvb',则停止
180207
if (origin.compareAbs(target) === 1) {
181208
result.value = format(end.value, dot.value)
182209
status.value = false

src/views/components/numbers/index.vue

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
:size="48"
2222
:break-all="form.breakAll"
2323
:classes="'d-text'"
24+
split=","
2425
>
2526
<template #prefix>{{ form.prefix }}</template>
2627
<template #suffix>{{ form.suffix }}</template>

0 commit comments

Comments
 (0)