11import { useMemo } from 'react' ;
22
3- export function createWheelCharacterFunctions ( { startValue, targetValue, decimalPlaces = 0 , locale = 'en' , useGrouping = false } ) {
3+ export function useWheelCharacters ( {
4+ startValue, targetValue, decimalPlaces = 0 , locale = 'en' , useGrouping = false
5+ } ) {
6+ return useMemo (
7+ ( ) => createWheelCharacterFunctions ( { startValue, targetValue, decimalPlaces, locale, useGrouping} ) ,
8+ [ startValue , targetValue , decimalPlaces , locale , useGrouping ]
9+ ) ;
10+ }
11+
12+ export function createWheelCharacterFunctions ( {
13+ startValue, targetValue, decimalPlaces = 0 , locale = 'en' , useGrouping = false
14+ } ) {
415 const hasNegative = startValue < 0 || targetValue < 0 ;
516 const crossesZero = ( startValue > 0 && targetValue < 0 ) || ( startValue < 0 && targetValue > 0 ) ;
617 const absStartValue = Math . abs ( startValue ) ;
@@ -9,8 +20,6 @@ export function createWheelCharacterFunctions({startValue, targetValue, decimalP
920 String ( Math . round ( absTargetValue ) ) . length ,
1021 String ( Math . round ( absStartValue ) ) . length
1122 ) ;
12- const delta = absTargetValue - absStartValue ;
13- const range = targetValue - startValue ;
1423
1524 const formatted = absTargetValue . toLocaleString ( locale , {
1625 minimumIntegerDigits : integerDigitCount ,
@@ -19,55 +28,65 @@ export function createWheelCharacterFunctions({startValue, targetValue, decimalP
1928 useGrouping
2029 } ) ;
2130
22- const charFunctions = [ ] ;
2331 let digitIndex = 0 ;
2432
25- for ( const char of formatted ) {
33+ const charFunctions = [ ... formatted ] . map ( ( char ) => {
2634 if ( / \d / . test ( char ) ) {
27- const position = integerDigitCount - 1 - digitIndex ;
35+ const position = integerDigitCount - digitIndex ++ - 1 ;
2836 const divisor = Math . pow ( 10 , position ) ;
2937
3038 if ( crossesZero ) {
31- charFunctions . push ( ( absValue ) => ( {
32- value : ( absValue / divisor ) % 10 ,
33- hideZero : position > 0 && absValue < divisor
34- } ) ) ;
35- } else {
36- const startDigit = Math . floor ( absStartValue / divisor ) % 10 ;
37- const endDigit = Math . floor ( absTargetValue / divisor ) % 10 ;
38- const fullRotations = Math . floor ( absTargetValue / ( divisor * 10 ) ) -
39- Math . floor ( absStartValue / ( divisor * 10 ) ) ;
40- let distance = endDigit - startDigit + fullRotations * 10 ;
41- if ( delta < 0 && endDigit > startDigit ) distance -= 10 ;
39+ const toZero = createDigitCharFunction ( position , divisor , absStartValue , 0 ) ;
40+ const fromZero = createDigitCharFunction ( position , divisor , 0 , absTargetValue ) ;
41+ const inFirstSegment = ( value ) => startValue < 0 ? value < 0 : value > 0 ;
4242
43- charFunctions . push ( ( absValue , progress ) => ( {
44- value : ( ( startDigit + progress * distance ) % 10 + 10 ) % 10 ,
45- hideZero : position > 0 && absValue < divisor
46- } ) ) ;
43+ return ( value , progress ) =>
44+ inFirstSegment ( value ) ?
45+ toZero ( value , ( value - startValue ) / - startValue ) :
46+ fromZero ( value , value / targetValue ) ;
47+ } else {
48+ return createDigitCharFunction ( position , divisor , absStartValue , absTargetValue ) ;
4749 }
48- digitIndex ++ ;
4950 } else if ( digitIndex < integerDigitCount ) {
5051 const threshold = Math . pow ( 10 , integerDigitCount - digitIndex ) ;
51- charFunctions . push ( ( absValue ) => ( { text : char , hide : absValue < threshold } ) ) ;
52+ return ( value ) => ( { text : char , hide : Math . abs ( value ) < threshold } ) ;
5253 } else {
53- charFunctions . push ( ( ) => ( { text : char } ) ) ;
54+ return ( ) => ( { text : char } ) ;
5455 }
55- }
56+ } ) ;
5657
5758 if ( hasNegative ) {
58- charFunctions . unshift ( ( absValue , progress , value ) => ( { text : '-' , hide : value > - 1 } ) ) ;
59+ const minusThreshold = - Math . pow ( 10 , - decimalPlaces ) ;
60+ charFunctions . unshift ( ( value ) => ( { text : '-' , hide : value > minusThreshold } ) ) ;
5961 }
6062
63+ const range = targetValue - startValue ;
64+
6165 return ( value ) => {
62- const absValue = Math . abs ( value ) ;
6366 const progress = range === 0 ? 0 : ( value - startValue ) / range ;
64- return charFunctions . map ( fn => fn ( absValue , progress , value ) ) ;
67+ return charFunctions . map ( fn => fn ( value , progress ) ) ;
6568 } ;
6669}
6770
68- export function useWheelCharacters ( { startValue, targetValue, decimalPlaces = 0 , locale = 'en' , useGrouping = false } ) {
69- return useMemo (
70- ( ) => createWheelCharacterFunctions ( { startValue, targetValue, decimalPlaces, locale, useGrouping} ) ,
71- [ startValue , targetValue , decimalPlaces , locale , useGrouping ]
72- ) ;
71+ function createDigitCharFunction ( position , divisor , segmentStart , segmentEnd ) {
72+ const startDigit = getDigitAtPosition ( segmentStart , divisor ) ;
73+ const endDigit = getDigitAtPosition ( segmentEnd , divisor ) ;
74+ const fullRotations = Math . floor ( segmentEnd / ( divisor * 10 ) ) -
75+ Math . floor ( segmentStart / ( divisor * 10 ) ) ;
76+ const distance = endDigit - startDigit + fullRotations * 10 ;
77+
78+ return ( value , progress ) => ( {
79+ value : ( ( startDigit + progress * distance ) % 10 + 10 ) % 10 ,
80+ hideZero : position > 0 && Math . abs ( value ) < divisor * 1.9
81+ } ) ;
82+ }
83+
84+ function getDigitAtPosition ( value , divisor ) {
85+ // Multiply by integer instead of dividing by fraction to avoid floating point errors
86+ // (e.g., 0.7 / 0.1 = 6.999... but 0.7 * 10 = 7)
87+ if ( divisor < 1 ) {
88+ const multiplier = Math . round ( 1 / divisor ) ;
89+ return Math . floor ( value * multiplier ) % 10 ;
90+ }
91+ return Math . floor ( value / divisor ) % 10 ;
7392}
0 commit comments