Skip to content

Commit e20fa76

Browse files
feat(component): Add CountTo component and Echart component
1 parent dad7330 commit e20fa76

File tree

15 files changed

+461
-48
lines changed

15 files changed

+461
-48
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"@zxcvbn-ts/core": "^1.2.0",
3131
"animate.css": "^4.1.1",
3232
"axios": "^0.25.0",
33+
"echarts": "^5.2.2",
34+
"echarts-wordcloud": "^2.0.0",
3335
"element-plus": "1.3.0-beta.5",
3436
"lodash-es": "^4.17.21",
3537
"mockjs": "^1.1.0",

pnpm-lock.yaml

Lines changed: 94 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/CountTo/index.ts

Whitespace-only changes.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<script setup lang="ts">
2+
import { reactive, computed, watch, onMounted, unref, toRef, PropType } from 'vue'
3+
import { isNumber } from '@/utils/is'
4+
import { propTypes } from '@/utils/propTypes'
5+
6+
const props = defineProps({
7+
startVal: propTypes.number.def(0),
8+
endVal: propTypes.number.def(2021),
9+
duration: propTypes.number.def(3000),
10+
autoplay: propTypes.bool.def(false),
11+
decimals: propTypes.number.validate((value: number) => value >= 0).def(0),
12+
decimal: propTypes.string.def('.'),
13+
separator: propTypes.string.def(','),
14+
prefix: propTypes.string.def(''),
15+
suffix: propTypes.string.def(''),
16+
useEasing: propTypes.bool.def(true),
17+
easingFn: {
18+
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
19+
default(t: number, b: number, c: number, d: number) {
20+
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
21+
}
22+
}
23+
})
24+
25+
const emit = defineEmits(['mounted', 'callback'])
26+
27+
const formatNumber = (num: number | string) => {
28+
const { decimals, decimal, separator, suffix, prefix } = props
29+
num = Number(num).toFixed(decimals)
30+
num += ''
31+
const x = num.split('.')
32+
let x1 = x[0]
33+
const x2 = x.length > 1 ? decimal + x[1] : ''
34+
const rgx = /(\d+)(\d{3})/
35+
if (separator && !isNumber(separator)) {
36+
while (rgx.test(x1)) {
37+
x1 = x1.replace(rgx, '$1' + separator + '$2')
38+
}
39+
}
40+
return prefix + x1 + x2 + suffix
41+
}
42+
43+
const state = reactive<{
44+
localStartVal: number
45+
printVal: number | null
46+
displayValue: string
47+
paused: boolean
48+
localDuration: number | null
49+
startTime: number | null
50+
timestamp: number | null
51+
rAF: any
52+
remaining: number | null
53+
}>({
54+
localStartVal: props.startVal,
55+
displayValue: formatNumber(props.startVal),
56+
printVal: null,
57+
paused: false,
58+
localDuration: props.duration,
59+
startTime: null,
60+
timestamp: null,
61+
remaining: null,
62+
rAF: null
63+
})
64+
65+
const displayValue = toRef(state, 'displayValue')
66+
67+
onMounted(() => {
68+
if (props.autoplay) {
69+
start()
70+
}
71+
emit('mounted')
72+
})
73+
74+
const getCountDown = computed(() => {
75+
return props.startVal > props.endVal
76+
})
77+
78+
watch([() => props.startVal, () => props.endVal], () => {
79+
if (props.autoplay) {
80+
start()
81+
}
82+
})
83+
84+
const start = () => {
85+
const { startVal, duration } = props
86+
state.localStartVal = startVal
87+
state.startTime = null
88+
state.localDuration = duration
89+
state.paused = false
90+
state.rAF = requestAnimationFrame(count)
91+
}
92+
93+
const pauseResume = () => {
94+
if (state.paused) {
95+
resume()
96+
state.paused = false
97+
} else {
98+
pause()
99+
state.paused = true
100+
}
101+
}
102+
103+
const pause = () => {
104+
cancelAnimationFrame(state.rAF)
105+
}
106+
107+
const resume = () => {
108+
state.startTime = null
109+
state.localDuration = +(state.remaining as number)
110+
state.localStartVal = +(state.printVal as number)
111+
requestAnimationFrame(count)
112+
}
113+
114+
const reset = () => {
115+
state.startTime = null
116+
cancelAnimationFrame(state.rAF)
117+
state.displayValue = formatNumber(props.startVal)
118+
}
119+
120+
const count = (timestamp: number) => {
121+
const { useEasing, easingFn, endVal } = props
122+
if (!state.startTime) state.startTime = timestamp
123+
state.timestamp = timestamp
124+
const progress = timestamp - state.startTime
125+
state.remaining = (state.localDuration as number) - progress
126+
if (useEasing) {
127+
if (unref(getCountDown)) {
128+
state.printVal =
129+
state.localStartVal -
130+
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number)
131+
} else {
132+
state.printVal = easingFn(
133+
progress,
134+
state.localStartVal,
135+
endVal - state.localStartVal,
136+
state.localDuration as number
137+
)
138+
}
139+
} else {
140+
if (unref(getCountDown)) {
141+
state.printVal =
142+
state.localStartVal -
143+
(state.localStartVal - endVal) * (progress / (state.localDuration as number))
144+
} else {
145+
state.printVal =
146+
state.localStartVal +
147+
(endVal - state.localStartVal) * (progress / (state.localDuration as number))
148+
}
149+
}
150+
if (unref(getCountDown)) {
151+
state.printVal = state.printVal < endVal ? endVal : state.printVal
152+
} else {
153+
state.printVal = state.printVal > endVal ? endVal : state.printVal
154+
}
155+
state.displayValue = formatNumber(state.printVal)
156+
if (progress < (state.localDuration as number)) {
157+
state.rAF = requestAnimationFrame(count)
158+
} else {
159+
emit('callback')
160+
}
161+
}
162+
163+
defineExpose({
164+
pauseResume,
165+
reset,
166+
start,
167+
pause
168+
})
169+
</script>
170+
171+
<template>
172+
<span>
173+
{{ displayValue }}
174+
</span>
175+
</template>

src/components/Echart/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Echart from './src/Echart.vue'
2+
3+
export { Echart }

0 commit comments

Comments
 (0)