refactor: new CountTo component with demo (#5551)

This commit is contained in:
Netfan
2025-02-17 21:16:10 +08:00
committed by GitHub
parent cddf71e600
commit 24bad09c74
6 changed files with 366 additions and 0 deletions

View File

@@ -0,0 +1,123 @@
<script lang="ts" setup>
import type { CountToProps } from './types';
import { computed, onMounted, ref, watch } from 'vue';
import { isString } from '@vben-core/shared/utils';
import { TransitionPresets, useTransition } from '@vueuse/core';
const props = withDefaults(defineProps<CountToProps>(), {
startVal: 0,
duration: 2000,
separator: ',',
decimal: '.',
decimals: 0,
delay: 0,
transition: () => TransitionPresets.easeOutExpo,
});
const emit = defineEmits(['started', 'finished']);
const lastValue = ref(props.startVal);
onMounted(() => {
lastValue.value = props.endVal;
});
watch(
() => props.endVal,
(val) => {
lastValue.value = val;
},
);
const currentValue = useTransition(lastValue, {
delay: computed(() => props.delay),
duration: computed(() => props.duration),
disabled: computed(() => props.disabled),
transition: computed(() => {
return isString(props.transition)
? TransitionPresets[props.transition]
: props.transition;
}),
onStarted() {
emit('started');
},
onFinished() {
emit('finished');
},
});
const numMain = computed(() => {
const result = currentValue.value
.toFixed(props.decimals)
.split('.')[0]
?.replaceAll(/\B(?=(\d{3})+(?!\d))/g, ',');
return result;
});
const numDec = computed(() => {
return (
props.decimal + currentValue.value.toFixed(props.decimals).split('.')[1]
);
});
</script>
<template>
<div class="count-to" v-bind="$attrs">
<slot name="prefix">
<div
class="count-to-prefix"
:style="prefixStyle"
:class="prefixClass"
v-if="prefix"
>
{{ prefix }}
</div>
</slot>
<div class="count-to-main" :class="mainClass" :style="mainStyle">
<span>{{ numMain }}</span>
<span
class="count-to-main-decimal"
v-if="decimals > 0"
:class="decimalClass"
:style="decimalStyle"
>
{{ numDec }}
</span>
</div>
<slot name="suffix">
<div
class="count-to-suffix"
:style="suffixStyle"
:class="suffixClass"
v-if="suffix"
>
{{ suffix }}
</div>
</slot>
</div>
</template>
<style lang="scss" scoped>
.count-to {
display: flex;
align-items: baseline;
&-prefix {
// font-size: 1rem;
}
&-suffix {
// font-size: 1rem;
}
&-main {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
// font-size: 1.5rem;
&-decimal {
// font-size: 0.8rem;
}
}
}
</style>

View File

@@ -0,0 +1,2 @@
export { default as CountTo } from './count-to.vue';
export * from './types';

View File

@@ -0,0 +1,53 @@
import type { CubicBezierPoints, EasingFunction } from '@vueuse/core';
import type { StyleValue } from 'vue';
import { TransitionPresets as TransitionPresetsData } from '@vueuse/core';
export type TransitionPresets = keyof typeof TransitionPresetsData;
export const TransitionPresetsKeys = Object.keys(
TransitionPresetsData,
) as TransitionPresets[];
export interface CountToProps {
/** 初始值 */
startVal?: number;
/** 当前值 */
endVal: number;
/** 是否禁用动画 */
disabled?: boolean;
/** 延迟动画开始的时间 */
delay?: number;
/** 持续时间 */
duration?: number;
/** 小数位数 */
decimals?: number;
/** 小数点 */
decimal?: string;
/** 分隔符 */
separator?: string;
/** 前缀 */
prefix?: string;
/** 后缀 */
suffix?: string;
/** 过渡效果 */
transition?: CubicBezierPoints | EasingFunction | TransitionPresets;
/** 整数部分的类名 */
mainClass?: string;
/** 小数部分的类名 */
decimalClass?: string;
/** 前缀部分的类名 */
prefixClass?: string;
/** 后缀部分的类名 */
suffixClass?: string;
/** 整数部分的样式 */
mainStyle?: StyleValue;
/** 小数部分的样式 */
decimalStyle?: StyleValue;
/** 前缀部分的样式 */
prefixStyle?: StyleValue;
/** 后缀部分的样式 */
suffixStyle?: StyleValue;
}

View File

@@ -1,6 +1,7 @@
export * from './api-component';
export * from './captcha';
export * from './col-page';
export * from './count-to';
export * from './ellipsis-text';
export * from './icon-picker';
export * from './json-viewer';