| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- <template>
- <VerticalScrollText
- v-if="type === 'scroller'"
- :numberString="finalString"
- :animDuration="duration"
- v-bind="props"
- />
- <Text v-else v-bind="props" :text="finalString" />
- </template>
- <script setup lang="ts">
- import Text, { type TextProps } from '@/components/basic/Text.vue';
- import VerticalScrollText from '@/components/typography/VerticalScrollText.vue';
- import { FormatUtils } from '@imengyu/imengyu-utils';
- import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
- export interface CountToProps extends TextProps {
- /**
- * 开始值
- * @default 0
- */
- startValue?: number;
- /**
- * 结束值
- */
- endValue: number;
- /**
- * 持续时间
- * @default 3000
- */
- duration?: number;
- /**
- * 是否将数字转换为千分符
- * @default false
- */
- thousand?: boolean;
- /**
- * 数字如果不足该位数,则在前面补0
- */
- numberCount?: number,
- /**
- * 保留小数位数
- * @default 0
- */
- decimalCount?: number,
- /**
- * 效果类型。默认:text
- * * text 普通文字切换效果
- * * scroller 文字上下滚动效果
- */
- type?: 'text'|'scroller';
- }
- export interface CountToInstance {
- restart: () => void;
- }
- const props = withDefaults(defineProps<CountToProps>(), {
- startValue: 0,
- endValue: 0,
- duration: 3000,
- thousand: false,
- numberCount: 0,
- decimalCount: 0,
- type: 'text',
- });
- const value = ref(0);
- const interval = ref(0);
- const speed = computed(() =>
- Math.max(1, Math.floor(Math.abs(props.startValue - props.endValue) / (props.duration / 50)))
- );
- function startAnim() {
- if (interval.value > 0)
- clearInterval(interval.value);
- if (props.startValue < props.endValue) {
- //+
- interval.value = setInterval(() => {
- if (value.value >= props.endValue) {
- clearInterval(interval.value);
- interval.value = 0;
- value.value = props.endValue;
- }
- else
- value.value = value.value + speed.value;
- }, 50) as unknown as number;
- } else {
- //-
- interval.value = setInterval(() => {
- if (value.value <= props.endValue) {
- clearInterval(interval.value);
- interval.value = 0;
- value.value = props.endValue;
- }
- else
- value.value = value.value - speed.value;
- }, 50) as unknown as number;
- }
- }
- const finalString = computed(() => {
- let valueString = props.decimalCount > 0 ? value.value.toFixed(props.decimalCount) : value.value.toString();
- if (valueString.length < props.numberCount)
- valueString = FormatUtils.formatNumberWithZero(
- Math.floor(value.value),
- props.numberCount
- ) + (valueString.split('.')[1] ?? '');
-
- //转为文字
- return (props.thousand ? FormatUtils.formatNumberWithComma(valueString) : valueString);
- })
- function loadAnim() {
- if (props.type === 'text') {
- value.value = props.startValue;
- startAnim();
- } else {
- value.value = props.endValue;
- }
- }
- watch(() => [ props.startValue, props.endValue, props.type ], () => {
- loadAnim();
- });
- onMounted(() => {
- loadAnim();
- })
- onBeforeUnmount(() => {
- if (interval.value > 0) {
- clearInterval(interval.value);
- interval.value = 0;
- }
- });
- defineExpose<CountToInstance>({
- restart() {
- if (props.type === 'text') {
- value.value = props.startValue;
- startAnim();
- } else {
- value.value = props.startValue;
- setTimeout(() => value.value = props.endValue, 200);
- }
- },
- });
- </script>
|