Skip to content

Commit

Permalink
feat(components): 新增 NumberTransition 数值动画组件
Browse files Browse the repository at this point in the history
  • Loading branch information
mengxinssfd committed Aug 5, 2023
1 parent 614ecef commit a56c193
Show file tree
Hide file tree
Showing 18 changed files with 454 additions and 6 deletions.
5 changes: 5 additions & 0 deletions internal/playground/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ export const baseRouter = [
path: '/dropdown',
element: getDemos(import.meta.glob('~/dropdown/demo/*.tsx')),
},
{
name: 'number-transition 数值动画',
path: '/number-transition',
element: getDemos(import.meta.glob('~/number-transition/demo/*.tsx')),
},
/*insert target*/
];

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@testing-library/user-event": "^14.4.3",
"@tool-pack/basic": "^0.1.1",
"@tool-pack/bom": "0.0.1-beta.0",
"@tool-pack/dom": "^0.0.8",
"@tool-pack/dom": "^0.0.9",
"@tool-pack/types": "^0.0.6",
"@types/fs-extra": "^11.0.1",
"@types/jest": "^29.5.1",
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
@import './collapse';
@import './option';
@import './dropdown';
@import './number-transition';
8 changes: 7 additions & 1 deletion packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export * from './space';
export * from './message';
export * from './divider';
export * from './drawer';
export { PLACEMENTS, PLACEMENTS_8, PLACEMENTS_12 } from '@pkg/shared';
export {
PLACEMENTS,
PLACEMENTS_8,
PLACEMENTS_12,
TIMING_FNS,
} from '@pkg/shared';
export * from './resizer';
export * from './word-balloon';
export * from './tooltip';
Expand All @@ -19,3 +24,4 @@ export * from './collapse-transition';
export * from './collapse';
export * from './option';
export * from './dropdown';
export * from './number-transition';
1 change: 1 addition & 0 deletions packages/components/src/namespace.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ $layout-header: '#{Var.$prefix}header';
$layout-footer: '#{Var.$prefix}footer';
$option: '#{Var.$prefix}option';
$dropdown: '#{Var.$prefix}dropdown';
$number-transition: '#{Var.$prefix}number-transition';
96 changes: 96 additions & 0 deletions packages/components/src/number-transition/NumberTransition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useEffect, useRef } from 'react';
import type { NumberTransitionProps } from './number-transition.types';
import { getComponentClass, useForceUpdate } from '@pkg/shared';
import type { RequiredPart } from '@tool-pack/types';
import { createTimeCountDown, getClassNames } from '@tool-pack/basic';
import { animateTo } from '@tool-pack/dom';

const rootName = getComponentClass('number-transition');
const defaultProps = {
duration: 3000,
from: 0,
to: 10,
precision: 0,
timingFunction: 'ease',
format: (value) => value,
} satisfies Partial<NumberTransitionProps>;

export const NumberTransition: React.FC<NumberTransitionProps> =
React.forwardRef<HTMLDivElement, NumberTransitionProps>((props, ref) => {
const {
duration,
active,
precision,
from,
to,
resetSignal,
timingFunction,
format,
onFinished,
attrs,
} = props as RequiredPart<NumberTransitionProps, keyof typeof defaultProps>;

const valueRef = useRef<number | string>(from);
const cancelerRef = useRef<() => void>();
const durationRef = useRef(duration);
const signalRef = useRef(resetSignal);
const forceUpdate = useForceUpdate();

const reset = () => {
cancelerRef.current?.();
valueRef.current = from;
durationRef.current = duration;
};

useEffect(() => {
if (!resetSignal === signalRef.current) return;
signalRef.current = resetSignal;
reset();
forceUpdate();
}, [resetSignal]);

useEffect(() => {
if (!active || durationRef.current <= 0) return;
cancelerRef.current?.();

const initValue = Number(valueRef.current);
const countDown = createTimeCountDown(durationRef.current);

const getValue: (value: number) => number | string =
precision === null
? (v) => v
: precision === 0
? (v) => Math.floor(v)
: (v) => v.toFixed(precision);

const { stop } = animateTo({
timeout: durationRef.current,
from: initValue,
to,
timingFn: timingFunction,
callback: (num) => {
valueRef.current = getValue(num);
forceUpdate();
},
after: onFinished,
});
return (cancelerRef.current = () => {
durationRef.current = countDown();
countDown.pause();
stop();
cancelerRef.current = undefined;
});
}, [active, from, duration, timingFunction, to, precision]);

return (
<div
{...attrs}
ref={ref}
className={getClassNames(rootName, attrs?.className)}>
{format(valueRef.current)}
</div>
);
});

NumberTransition.defaultProps = defaultProps;
NumberTransition.displayName = 'NumberTransition';
36 changes: 36 additions & 0 deletions packages/components/src/number-transition/demo/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* title: 基础用法
* description: NumberTransition 基础用法。
*/

import React, { useState } from 'react';
import { Button, NumberTransition, Space } from '@tool-pack/react-ui';

const App: React.FC = () => {
const [active, setActive] = useState(false);
const [disabled, setDisabled] = useState(false);

return (
<Space vertical>
<Space>
<Button
type="primary"
disabled={disabled}
onClick={() => setActive((v) => !v)}>
{active ? '暂停' : '启动'}
</Button>
</Space>
<NumberTransition
active={active}
from={30}
to={50}
onFinished={() => {
setDisabled(true);
setActive(false);
}}
/>
</Space>
);
};

export default App;
36 changes: 36 additions & 0 deletions packages/components/src/number-transition/demo/duration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* title: 动画时长
* description: 自定义动画时长,单位为毫秒。默认为 3000。
*/

import React, { useState } from 'react';
import { Button, NumberTransition, Space } from '@tool-pack/react-ui';

const App: React.FC = () => {
const [active, setActive] = useState(false);
const [disabled, setDisabled] = useState(false);

return (
<Space vertical>
<Space>
<Button
type="primary"
disabled={disabled}
onClick={() => setActive((v) => !v)}>
{active ? '暂停' : '启动'}
</Button>
</Space>
<NumberTransition
active={active}
duration={8000}
to={25}
onFinished={() => {
setDisabled(true);
setActive(false);
}}
/>
</Space>
);
};

export default App;
40 changes: 40 additions & 0 deletions packages/components/src/number-transition/demo/format.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* title: 格式化
* description: 对数字显示格式化。
*/

import React, { useState } from 'react';
import { Button, NumberTransition, Space } from '@tool-pack/react-ui';

const format = new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec');

const App: React.FC = () => {
const [active, setActive] = useState(false);
const [disabled, setDisabled] = useState(false);

return (
<Space vertical>
<Space>
<Button
type="primary"
disabled={disabled}
onClick={() => setActive((v) => !v)}>
{active ? '暂停' : '启动'}
</Button>
</Space>
<NumberTransition
active={active}
to={50}
format={(value) => {
return format.format(Number(value));
}}
onFinished={() => {
setDisabled(true);
setActive(false);
}}
/>
</Space>
);
};

export default App;
49 changes: 49 additions & 0 deletions packages/components/src/number-transition/demo/precision.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* title: 精度
* description: 小数位数。默认小数位数为 0。为 null 时保持原样输出。
*/

import React, { useState } from 'react';
import { Button, NumberTransition, Space } from '@tool-pack/react-ui';

const App: React.FC = () => {
const [active, setActive] = useState(false);
const [disabled, setDisabled] = useState(false);

return (
<Space vertical>
<Space>
<Button
type="primary"
disabled={disabled}
onClick={() => setActive((v) => !v)}>
{active ? '暂停' : '启动'}
</Button>
</Space>
<Space>
<div>小数位 2</div>
<NumberTransition
active={active}
precision={2}
onFinished={() => {
setDisabled(true);
setActive(false);
}}
/>
</Space>
<Space>
<div>小数位 null 保持原样</div>
<NumberTransition
active={active}
precision={null}
onFinished={() => {
setDisabled(true);
setActive(false);
}}
/>
</Space>
</Space>
);
};

export default App;
46 changes: 46 additions & 0 deletions packages/components/src/number-transition/demo/reset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* title: 重置
* description: 重置为初始状态。
*/

import React, { useState } from 'react';
import { Button, NumberTransition, Space } from '@tool-pack/react-ui';

const App: React.FC = () => {
const [active, setActive] = useState(false);
const [disabled, setDisabled] = useState(false);
const [signal, setSignal] = useState({});

const reset = () => {
setDisabled(false);
setActive(false);
setSignal({});
};

const onFinished = () => {
setDisabled(true);
setActive(false);
};

return (
<Space vertical>
<Space>
<Button
type="primary"
disabled={disabled}
onClick={() => setActive((v) => !v)}>
{active ? '暂停' : '启动'}
</Button>
<Button onClick={reset}>重置</Button>
</Space>
<NumberTransition
resetSignal={signal}
active={active}
to={50}
onFinished={onFinished}
/>
</Space>
);
};

export default App;
Loading

0 comments on commit a56c193

Please sign in to comment.