123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- <template>
- <span>
- {{ displayValue }}
- </span>
- </template>
- <script lang="ts">
- import { defineComponent, reactive, computed, watch, onMounted, unref, toRef } from 'vue';
- import { countToProps } from './props';
- import { useRaf } from '/@/hooks/event/useRaf';
- import { isNumber } from '/@/utils/is';
- export default defineComponent({
- name: 'CountTo',
- props: countToProps,
- emits: ['mounted', 'callback'],
- setup(props, { emit }) {
- const { requestAnimationFrame, cancelAnimationFrame } = useRaf();
- 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>
|