-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathSingleNumber.tsx
132 lines (119 loc) · 3.31 KB
/
SingleNumber.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import type { CSSProperties } from 'vue';
import { computed, defineComponent, onUnmounted, reactive, ref, watch } from 'vue';
import classNames from '../_util/classNames';
export interface UnitNumberProps {
prefixCls: string;
value: string | number;
offset?: number;
current?: boolean;
}
function UnitNumber({ prefixCls, value, current, offset = 0 }: UnitNumberProps) {
let style: CSSProperties | undefined;
if (offset) {
style = {
position: 'absolute',
top: `${offset}00%`,
left: 0,
};
}
return (
<p
style={style}
class={classNames(`${prefixCls}-only-unit`, {
current,
})}
>
{value}
</p>
);
}
function getOffset(start: number, end: number, unit: -1 | 1) {
let index = start;
let offset = 0;
while ((index + 10) % 10 !== end) {
index += unit;
offset += unit;
}
return offset;
}
export default defineComponent({
name: 'SingleNumber',
props: {
prefixCls: String,
value: String,
count: Number,
},
setup(props) {
const originValue = computed(() => Number(props.value));
const originCount = computed(() => Math.abs(props.count));
const state = reactive({
prevValue: originValue.value,
prevCount: originCount.value,
});
// ============================= Events =============================
const onTransitionEnd = () => {
state.prevValue = originValue.value;
state.prevCount = originCount.value;
};
const timeout = ref();
// Fallback if transition event not support
watch(
originValue,
() => {
clearTimeout(timeout.value);
timeout.value = setTimeout(() => {
onTransitionEnd();
}, 1000);
},
{ flush: 'post' },
);
onUnmounted(() => {
clearTimeout(timeout.value);
});
return () => {
let unitNodes: any[];
let offsetStyle: CSSProperties = {};
const value = originValue.value;
if (state.prevValue === value || Number.isNaN(value) || Number.isNaN(state.prevValue)) {
// Nothing to change
unitNodes = [UnitNumber({ ...props, current: true } as UnitNumberProps)];
offsetStyle = {
transition: 'none',
};
} else {
unitNodes = [];
// Fill basic number units
const end = value + 10;
const unitNumberList: number[] = [];
for (let index = value; index <= end; index += 1) {
unitNumberList.push(index);
}
// Fill with number unit nodes
const prevIndex = unitNumberList.findIndex(n => n % 10 === state.prevValue);
unitNodes = unitNumberList.map((n, index) => {
const singleUnit = n % 10;
return UnitNumber({
...props,
value: singleUnit,
offset: index - prevIndex,
current: index === prevIndex,
} as UnitNumberProps);
});
// Calculate container offset value
const unit = state.prevCount < originCount.value ? 1 : -1;
offsetStyle = {
transform: `translateY(${-getOffset(state.prevValue, value, unit)}00%)`,
};
}
return (
<span
class={`${props.prefixCls}-only`}
style={offsetStyle}
onTransitionend={() => onTransitionEnd()}
>
{unitNodes}
</span>
);
};
},
});