mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2026-01-26 17:02:00 +08:00
feat: add countTo components
This commit is contained in:
172
src/components/countTo/src/index.vue
Normal file
172
src/components/countTo/src/index.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<span>{{ displayValue }}</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
unref,
|
||||
toRef
|
||||
} from "vue";
|
||||
import { countToProps } from "./props";
|
||||
import { isNumber } from "/@/utils/is";
|
||||
export default defineComponent({
|
||||
name: "CountTo",
|
||||
props: countToProps,
|
||||
emits: ["mounted", "callback"],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive<{
|
||||
localStartVal: number;
|
||||
printVal: number | null;
|
||||
displayValue: string;
|
||||
paused: boolean;
|
||||
localDuration: number | null;
|
||||
startTime: number | null;
|
||||
timestamp: number | null;
|
||||
rAF: any;
|
||||
remaining: number | null;
|
||||
}>({
|
||||
localStartVal: props.startVal,
|
||||
displayValue: formatNumber(props.startVal),
|
||||
printVal: null,
|
||||
paused: false,
|
||||
localDuration: props.duration,
|
||||
startTime: null,
|
||||
timestamp: null,
|
||||
remaining: null,
|
||||
rAF: null
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
emit("mounted");
|
||||
});
|
||||
|
||||
const getCountDown = computed(() => {
|
||||
return props.startVal > props.endVal;
|
||||
});
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
function start() {
|
||||
const { startVal, duration } = props;
|
||||
state.localStartVal = startVal;
|
||||
state.startTime = null;
|
||||
state.localDuration = duration;
|
||||
state.paused = false;
|
||||
state.rAF = requestAnimationFrame(count);
|
||||
}
|
||||
|
||||
function pauseResume() {
|
||||
if (state.paused) {
|
||||
resume();
|
||||
state.paused = false;
|
||||
} else {
|
||||
pause();
|
||||
state.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
cancelAnimationFrame(state.rAF);
|
||||
}
|
||||
|
||||
function resume() {
|
||||
state.startTime = null;
|
||||
state.localDuration = +(state.remaining as number);
|
||||
state.localStartVal = +(state.printVal as number);
|
||||
requestAnimationFrame(count);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.startTime = null;
|
||||
cancelAnimationFrame(state.rAF);
|
||||
state.displayValue = formatNumber(props.startVal);
|
||||
}
|
||||
|
||||
function count(timestamp: number) {
|
||||
const { useEasing, easingFn, endVal } = props;
|
||||
if (!state.startTime) state.startTime = timestamp;
|
||||
state.timestamp = timestamp;
|
||||
const progress = timestamp - state.startTime;
|
||||
state.remaining = (state.localDuration as number) - progress;
|
||||
if (useEasing) {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
easingFn(
|
||||
progress,
|
||||
0,
|
||||
state.localStartVal - endVal,
|
||||
state.localDuration as number
|
||||
);
|
||||
} else {
|
||||
state.printVal = easingFn(
|
||||
progress,
|
||||
state.localStartVal,
|
||||
endVal - state.localStartVal,
|
||||
state.localDuration as number
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
(state.localStartVal - endVal) *
|
||||
(progress / (state.localDuration as number));
|
||||
} else {
|
||||
state.printVal =
|
||||
state.localStartVal +
|
||||
(endVal - state.localStartVal) *
|
||||
(progress / (state.localDuration as number));
|
||||
}
|
||||
}
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal = state.printVal < endVal ? endVal : state.printVal;
|
||||
} else {
|
||||
state.printVal = state.printVal > endVal ? endVal : state.printVal;
|
||||
}
|
||||
state.displayValue = formatNumber(state.printVal);
|
||||
if (progress < (state.localDuration as number)) {
|
||||
state.rAF = requestAnimationFrame(count);
|
||||
} else {
|
||||
emit("callback");
|
||||
}
|
||||
}
|
||||
|
||||
function formatNumber(num: number | string) {
|
||||
const { decimals, decimal, separator, suffix, prefix } = props;
|
||||
num = Number(num).toFixed(decimals);
|
||||
num += "";
|
||||
const x = num.split(".");
|
||||
let x1 = x[0];
|
||||
const x2 = x.length > 1 ? decimal + x[1] : "";
|
||||
const rgx = /(\d+)(\d{3})/;
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, "$1" + separator + "$2");
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix;
|
||||
}
|
||||
|
||||
return {
|
||||
count,
|
||||
reset,
|
||||
resume,
|
||||
start,
|
||||
pauseResume,
|
||||
displayValue: toRef(state, "displayValue")
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
27
src/components/countTo/src/props.ts
Normal file
27
src/components/countTo/src/props.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { PropType } from 'vue'
|
||||
import { propTypes } from '/@/utils/propTypes'
|
||||
export const countToProps = {
|
||||
startVal: propTypes.number.def(0),
|
||||
endVal: propTypes.number.def(2020),
|
||||
duration: propTypes.number.def(1300),
|
||||
autoplay: propTypes.bool.def(true),
|
||||
decimals: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 0,
|
||||
validator(value: number) {
|
||||
return value >= 0
|
||||
},
|
||||
},
|
||||
decimal: propTypes.string.def('.'),
|
||||
separator: propTypes.string.def(','),
|
||||
prefix: propTypes.string.def(''),
|
||||
suffix: propTypes.string.def(''),
|
||||
useEasing: propTypes.bool.def(true),
|
||||
easingFn: {
|
||||
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
|
||||
default(t: number, b: number, c: number, d: number) {
|
||||
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user