SingleNumber.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. "use client";
  2. import * as React from 'react';
  3. import classNames from 'classnames';
  4. const UnitNumber = props => {
  5. const {
  6. prefixCls,
  7. value,
  8. current,
  9. offset = 0
  10. } = props;
  11. let style;
  12. if (offset) {
  13. style = {
  14. position: 'absolute',
  15. top: `${offset}00%`,
  16. left: 0
  17. };
  18. }
  19. return /*#__PURE__*/React.createElement("span", {
  20. style: style,
  21. className: classNames(`${prefixCls}-only-unit`, {
  22. current
  23. })
  24. }, value);
  25. };
  26. function getOffset(start, end, unit) {
  27. let index = start;
  28. let offset = 0;
  29. while ((index + 10) % 10 !== end) {
  30. index += unit;
  31. offset += unit;
  32. }
  33. return offset;
  34. }
  35. const SingleNumber = props => {
  36. const {
  37. prefixCls,
  38. count: originCount,
  39. value: originValue
  40. } = props;
  41. const value = Number(originValue);
  42. const count = Math.abs(originCount);
  43. const [prevValue, setPrevValue] = React.useState(value);
  44. const [prevCount, setPrevCount] = React.useState(count);
  45. // ============================= Events =============================
  46. const onTransitionEnd = () => {
  47. setPrevValue(value);
  48. setPrevCount(count);
  49. };
  50. // Fallback if transition events are not supported
  51. React.useEffect(() => {
  52. const timer = setTimeout(onTransitionEnd, 1000);
  53. return () => clearTimeout(timer);
  54. }, [value]);
  55. // ============================= Render =============================
  56. // Render unit list
  57. let unitNodes;
  58. let offsetStyle;
  59. if (prevValue === value || Number.isNaN(value) || Number.isNaN(prevValue)) {
  60. // Nothing to change
  61. unitNodes = [/*#__PURE__*/React.createElement(UnitNumber, Object.assign({}, props, {
  62. key: value,
  63. current: true
  64. }))];
  65. offsetStyle = {
  66. transition: 'none'
  67. };
  68. } else {
  69. unitNodes = [];
  70. // Fill basic number units
  71. const end = value + 10;
  72. const unitNumberList = [];
  73. for (let index = value; index <= end; index += 1) {
  74. unitNumberList.push(index);
  75. }
  76. const unit = prevCount < count ? 1 : -1;
  77. // Fill with number unit nodes
  78. const prevIndex = unitNumberList.findIndex(n => n % 10 === prevValue);
  79. // Cut list
  80. const cutUnitNumberList = unit < 0 ? unitNumberList.slice(0, prevIndex + 1) : unitNumberList.slice(prevIndex);
  81. unitNodes = cutUnitNumberList.map((n, index) => {
  82. const singleUnit = n % 10;
  83. return /*#__PURE__*/React.createElement(UnitNumber, Object.assign({}, props, {
  84. key: n,
  85. value: singleUnit,
  86. offset: unit < 0 ? index - prevIndex : index,
  87. current: index === prevIndex
  88. }));
  89. });
  90. // Calculate container offset value
  91. offsetStyle = {
  92. transform: `translateY(${-getOffset(prevValue, value, unit)}00%)`
  93. };
  94. }
  95. return /*#__PURE__*/React.createElement("span", {
  96. className: `${prefixCls}-only`,
  97. style: offsetStyle,
  98. onTransitionEnd: onTransitionEnd
  99. }, unitNodes);
  100. };
  101. export default SingleNumber;