@@ -17,6 +17,7 @@ import Animated, {
17
17
withTiming ,
18
18
} from 'react-native-reanimated' ;
19
19
import { Path , Svg } from 'react-native-svg' ;
20
+
20
21
import ChartContext , {
21
22
useGenerateValues as generateValues ,
22
23
} from '../../helpers/ChartContext' ;
@@ -64,16 +65,18 @@ function combineConfigs(a, b) {
64
65
return r ;
65
66
}
66
67
67
- const parse = data => {
68
+ const parse = ( data , yRange ) => {
68
69
const { greatestY, smallestY } = findYExtremes ( data ) ;
70
+ const minY = yRange ? yRange [ 0 ] : smallestY . y ;
71
+ const maxY = yRange ? yRange [ 1 ] : greatestY . y ;
69
72
const smallestX = data [ 0 ] ;
70
73
const greatestX = data [ data . length - 1 ] ;
71
74
return [
72
75
data . map ( ( { x, y } ) => ( {
73
76
originalX : x ,
74
77
originalY : y ,
75
78
x : ( x - smallestX . x ) / ( greatestX . x - smallestX . x ) ,
76
- y : 1 - ( y - smallestY . y ) / ( greatestY . y - smallestY . y ) ,
79
+ y : 1 - ( y - minY ) / ( maxY - minY ) ,
77
80
} ) ) ,
78
81
{
79
82
greatestX,
@@ -101,6 +104,13 @@ function setoriginalXYAccordingToPosition(
101
104
idx = data . value . length - 1 ;
102
105
}
103
106
}
107
+ if ( ! data . value [ idx ] ) {
108
+ // prevent the following error on android:
109
+ // java.lang.RuntimeException: undefined is not an object (evaluating 'data.value[idx].originalX')
110
+ // why data.value = [] sometimes onActive?
111
+ console . warn ( 'No data available for chart' , data . value . length , idx ) ;
112
+ return ;
113
+ }
104
114
originalX . value = data . value [ idx ] . originalX . toString ( ) ;
105
115
originalY . value = data . value [ idx ] . originalY
106
116
? data . value [ idx ] . originalY . toString ( )
@@ -197,20 +207,19 @@ export default function ChartPathProvider({
197
207
} else {
198
208
setData ( providedData ) ;
199
209
}
200
- // eslint-disable-next-line react-hooks/exhaustive-deps
201
210
} , [ providedData ] ) ;
202
211
203
212
const smoothingStrategy = useSharedValue ( data . smoothingStrategy ) ;
204
213
205
214
useEffect ( ( ) => {
206
- if ( ! data || ! data . points ) {
215
+ if ( ! data || ! data . points || data . points . length === 0 ) {
207
216
return ;
208
217
}
209
- const [ parsedData ] = parse ( data . points ) ;
218
+ const [ parsedData ] = parse ( data . points , data . yRange ) ;
210
219
const [ parsedoriginalData , newExtremes ] = parse (
211
220
data . nativePoints || data . points
212
221
) ;
213
- setContextValue ( prev => ( { ...prev , ...newExtremes , data } ) ) ;
222
+ setContextValue ( ( prev ) => ( { ...prev , ...newExtremes , data } ) ) ;
214
223
setExtremes ( newExtremes ) ;
215
224
if ( prevData . value . length !== 0 ) {
216
225
valuesStore . current . prevData = currData . value ;
@@ -248,13 +257,12 @@ export default function ChartPathProvider({
248
257
currData . value = parsedData ;
249
258
curroriginalData . value = parsedoriginalData ;
250
259
}
251
- // eslint-disable-next-line react-hooks/exhaustive-deps
252
260
} , [ data ] ) ;
253
261
254
262
const isStarted = useSharedValue ( false , 'isStarted' ) ;
255
263
256
264
const onLongPressGestureEvent = useAnimatedGestureHandler ( {
257
- onActive : event => {
265
+ onActive : ( event ) => {
258
266
state . value = event . state ;
259
267
if ( ! currData . value || currData . value . length === 0 ) {
260
268
return ;
@@ -282,7 +290,7 @@ export default function ChartPathProvider({
282
290
) ;
283
291
284
292
let idx = 0 ;
285
- let ss = smoothingStrategy ;
293
+ const ss = smoothingStrategy ;
286
294
for ( let i = 0 ; i < currData . value . length ; i ++ ) {
287
295
if ( getValue ( currData , i , ss ) . x > eventX / layoutSize . value . width ) {
288
296
idx = i ;
@@ -332,7 +340,7 @@ export default function ChartPathProvider({
332
340
) ;
333
341
positionX . value = eventX ;
334
342
} ,
335
- onCancel : event => {
343
+ onCancel : ( event ) => {
336
344
isStarted . value = false ;
337
345
state . value = event . state ;
338
346
originalX . value = '' ;
@@ -350,7 +358,7 @@ export default function ChartPathProvider({
350
358
) ;
351
359
}
352
360
} ,
353
- onEnd : event => {
361
+ onEnd : ( event ) => {
354
362
isStarted . value = false ;
355
363
state . value = event . state ;
356
364
originalX . value = '' ;
@@ -372,7 +380,7 @@ export default function ChartPathProvider({
372
380
impactHeavy ( ) ;
373
381
}
374
382
} ,
375
- onFail : event => {
383
+ onFail : ( event ) => {
376
384
isStarted . value = false ;
377
385
state . value = event . state ;
378
386
originalX . value = '' ;
@@ -390,11 +398,28 @@ export default function ChartPathProvider({
390
398
) ;
391
399
}
392
400
} ,
393
- onStart : event => {
401
+ onStart : ( event ) => {
402
+ // WARNING: the following code does not run on using iOS, but it does on Android.
403
+ // I use the same code from onActive except of "progress.value = 1" which was taken from the original onStart.
394
404
state . value = event . state ;
395
405
if ( ! currData . value || currData . value . length === 0 ) {
396
406
return ;
397
407
}
408
+ if ( ! isStarted . value ) {
409
+ dotScale . value = withSpring (
410
+ 1 ,
411
+ combineConfigs ( springDefaultConfig , springConfig )
412
+ ) ;
413
+ pathOpacity . value = withTiming (
414
+ 0 ,
415
+ combineConfigs ( timingFeedbackDefaultConfig , timingFeedbackConfig )
416
+ ) ;
417
+ }
418
+
419
+ if ( hapticsEnabledValue . value && ! isStarted . value ) {
420
+ impactHeavy ( ) ;
421
+ }
422
+ isStarted . value = true ;
398
423
399
424
const eventX = positionXWithMargin (
400
425
event . x ,
@@ -404,46 +429,58 @@ export default function ChartPathProvider({
404
429
405
430
progress . value = 1 ;
406
431
let idx = 0 ;
432
+ const ss = smoothingStrategy ;
407
433
for ( let i = 0 ; i < currData . value . length ; i ++ ) {
408
- if ( currData . value [ i ] . x > eventX / layoutSize . value . width ) {
434
+ if ( getValue ( currData , i , ss ) . x > eventX / layoutSize . value . width ) {
409
435
idx = i ;
410
436
break ;
411
437
}
412
438
if ( i === currData . value . length - 1 ) {
413
439
idx = currData . value . length - 1 ;
414
440
}
415
441
}
442
+
443
+ if (
444
+ ss . value === 'bezier' &&
445
+ currData . value . length > 30 &&
446
+ eventX / layoutSize . value . width >=
447
+ currData . value [ currData . value . length - 2 ] . x
448
+ ) {
449
+ const prevLastY = currData . value [ currData . value . length - 2 ] . y ;
450
+ const prevLastX = currData . value [ currData . value . length - 2 ] . x ;
451
+ const lastY = currData . value [ currData . value . length - 1 ] . y ;
452
+ const lastX = currData . value [ currData . value . length - 1 ] . x ;
453
+ const progress =
454
+ ( eventX / layoutSize . value . width - prevLastX ) / ( lastX - prevLastX ) ;
455
+ positionY . value =
456
+ ( prevLastY + progress * ( lastY - prevLastY ) ) *
457
+ layoutSize . value . height ;
458
+ } else if ( idx === 0 ) {
459
+ positionY . value =
460
+ getValue ( currData , idx , ss ) . y * layoutSize . value . height ;
461
+ } else {
462
+ // prev + diff over X
463
+ positionY . value =
464
+ ( getValue ( currData , idx - 1 , ss ) . y +
465
+ ( getValue ( currData , idx , ss ) . y -
466
+ getValue ( currData , idx - 1 , ss ) . y ) *
467
+ ( ( eventX / layoutSize . value . width -
468
+ getValue ( currData , idx - 1 , ss ) . x ) /
469
+ ( getValue ( currData , idx , ss ) . x -
470
+ getValue ( currData , idx - 1 , ss ) . x ) ) ) *
471
+ layoutSize . value . height ;
472
+ }
473
+
416
474
setoriginalXYAccordingToPosition (
417
475
originalX ,
418
476
originalY ,
419
477
eventX / layoutSize . value . width ,
420
478
curroriginalData
421
479
) ;
422
- dotScale . value = withSpring (
423
- 1 ,
424
- combineConfigs ( springDefaultConfig , springConfig )
425
- ) ;
426
-
427
- if ( ! android ) {
428
- positionX . value = positionXWithMargin (
429
- eventX ,
430
- 30 ,
431
- layoutSize . value . width
432
- ) ;
433
- positionY . value = currData . value [ idx ] . y * layoutSize . value . height ;
434
- pathOpacity . value = withTiming (
435
- 0 ,
436
- combineConfigs ( timingFeedbackDefaultConfig , timingFeedbackConfig )
437
- ) ;
438
- }
439
- if ( hapticsEnabledValue . value && ! isStarted . value ) {
440
- impactHeavy ( ) ;
441
- }
442
- isStarted . value = true ;
480
+ positionX . value = eventX ;
443
481
} ,
444
482
} ) ;
445
483
446
- // @ts -ignore
447
484
const dotStyle = useAnimatedStyle (
448
485
( ) => ( {
449
486
opacity : dotScale . value ,
@@ -521,7 +558,7 @@ function ChartPath({
521
558
let toValue = currData . value ;
522
559
let res ;
523
560
let smoothing = 0 ;
524
- let strategy = smoothingStrategy . value ;
561
+ const strategy = smoothingStrategy . value ;
525
562
if ( progress . value !== 1 ) {
526
563
const numOfPoints = Math . round (
527
564
fromValue . length +
0 commit comments