Skip to content

Commit

Permalink
Add yRange support again, code linting and bugfixes on android. (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
outaTiME authored Apr 21, 2021
1 parent 3293364 commit 6f57ff3
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 40 deletions.
1 change: 1 addition & 0 deletions src/charts/linear/ChartDot.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useContext } from 'react';
import Animated from 'react-native-reanimated';

import ChartContext from '../../helpers/ChartContext';
import withReanimatedFallback from '../../helpers/withReanimatedFallback';

Expand Down
1 change: 1 addition & 0 deletions src/charts/linear/ChartLabels.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Animated, {
useAnimatedStyle,
useDerivedValue,
} from 'react-native-reanimated';

import ChartContext from '../../helpers/ChartContext';

const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
Expand Down
111 changes: 74 additions & 37 deletions src/charts/linear/ChartPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Animated, {
withTiming,
} from 'react-native-reanimated';
import { Path, Svg } from 'react-native-svg';

import ChartContext, {
useGenerateValues as generateValues,
} from '../../helpers/ChartContext';
Expand Down Expand Up @@ -64,16 +65,18 @@ function combineConfigs(a, b) {
return r;
}

const parse = data => {
const parse = (data, yRange) => {
const { greatestY, smallestY } = findYExtremes(data);
const minY = yRange ? yRange[0] : smallestY.y;
const maxY = yRange ? yRange[1] : greatestY.y;
const smallestX = data[0];
const greatestX = data[data.length - 1];
return [
data.map(({ x, y }) => ({
originalX: x,
originalY: y,
x: (x - smallestX.x) / (greatestX.x - smallestX.x),
y: 1 - (y - smallestY.y) / (greatestY.y - smallestY.y),
y: 1 - (y - minY) / (maxY - minY),
})),
{
greatestX,
Expand Down Expand Up @@ -101,6 +104,13 @@ function setoriginalXYAccordingToPosition(
idx = data.value.length - 1;
}
}
if (!data.value[idx]) {
// prevent the following error on android:
// java.lang.RuntimeException: undefined is not an object (evaluating 'data.value[idx].originalX')
// why data.value = [] sometimes onActive?
console.warn('No data available for chart', data.value.length, idx);
return;
}
originalX.value = data.value[idx].originalX.toString();
originalY.value = data.value[idx].originalY
? data.value[idx].originalY.toString()
Expand Down Expand Up @@ -197,20 +207,19 @@ export default function ChartPathProvider({
} else {
setData(providedData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [providedData]);

const smoothingStrategy = useSharedValue(data.smoothingStrategy);

useEffect(() => {
if (!data || !data.points) {
if (!data || !data.points || data.points.length === 0) {
return;
}
const [parsedData] = parse(data.points);
const [parsedData] = parse(data.points, data.yRange);
const [parsedoriginalData, newExtremes] = parse(
data.nativePoints || data.points
);
setContextValue(prev => ({ ...prev, ...newExtremes, data }));
setContextValue((prev) => ({ ...prev, ...newExtremes, data }));
setExtremes(newExtremes);
if (prevData.value.length !== 0) {
valuesStore.current.prevData = currData.value;
Expand Down Expand Up @@ -248,13 +257,12 @@ export default function ChartPathProvider({
currData.value = parsedData;
curroriginalData.value = parsedoriginalData;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data]);

const isStarted = useSharedValue(false, 'isStarted');

const onLongPressGestureEvent = useAnimatedGestureHandler({
onActive: event => {
onActive: (event) => {
state.value = event.state;
if (!currData.value || currData.value.length === 0) {
return;
Expand Down Expand Up @@ -282,7 +290,7 @@ export default function ChartPathProvider({
);

let idx = 0;
let ss = smoothingStrategy;
const ss = smoothingStrategy;
for (let i = 0; i < currData.value.length; i++) {
if (getValue(currData, i, ss).x > eventX / layoutSize.value.width) {
idx = i;
Expand Down Expand Up @@ -332,7 +340,7 @@ export default function ChartPathProvider({
);
positionX.value = eventX;
},
onCancel: event => {
onCancel: (event) => {
isStarted.value = false;
state.value = event.state;
originalX.value = '';
Expand All @@ -350,7 +358,7 @@ export default function ChartPathProvider({
);
}
},
onEnd: event => {
onEnd: (event) => {
isStarted.value = false;
state.value = event.state;
originalX.value = '';
Expand All @@ -372,7 +380,7 @@ export default function ChartPathProvider({
impactHeavy();
}
},
onFail: event => {
onFail: (event) => {
isStarted.value = false;
state.value = event.state;
originalX.value = '';
Expand All @@ -390,11 +398,28 @@ export default function ChartPathProvider({
);
}
},
onStart: event => {
onStart: (event) => {
// WARNING: the following code does not run on using iOS, but it does on Android.
// I use the same code from onActive except of "progress.value = 1" which was taken from the original onStart.
state.value = event.state;
if (!currData.value || currData.value.length === 0) {
return;
}
if (!isStarted.value) {
dotScale.value = withSpring(
1,
combineConfigs(springDefaultConfig, springConfig)
);
pathOpacity.value = withTiming(
0,
combineConfigs(timingFeedbackDefaultConfig, timingFeedbackConfig)
);
}

if (hapticsEnabledValue.value && !isStarted.value) {
impactHeavy();
}
isStarted.value = true;

const eventX = positionXWithMargin(
event.x,
Expand All @@ -404,46 +429,58 @@ export default function ChartPathProvider({

progress.value = 1;
let idx = 0;
const ss = smoothingStrategy;
for (let i = 0; i < currData.value.length; i++) {
if (currData.value[i].x > eventX / layoutSize.value.width) {
if (getValue(currData, i, ss).x > eventX / layoutSize.value.width) {
idx = i;
break;
}
if (i === currData.value.length - 1) {
idx = currData.value.length - 1;
}
}

if (
ss.value === 'bezier' &&
currData.value.length > 30 &&
eventX / layoutSize.value.width >=
currData.value[currData.value.length - 2].x
) {
const prevLastY = currData.value[currData.value.length - 2].y;
const prevLastX = currData.value[currData.value.length - 2].x;
const lastY = currData.value[currData.value.length - 1].y;
const lastX = currData.value[currData.value.length - 1].x;
const progress =
(eventX / layoutSize.value.width - prevLastX) / (lastX - prevLastX);
positionY.value =
(prevLastY + progress * (lastY - prevLastY)) *
layoutSize.value.height;
} else if (idx === 0) {
positionY.value =
getValue(currData, idx, ss).y * layoutSize.value.height;
} else {
// prev + diff over X
positionY.value =
(getValue(currData, idx - 1, ss).y +
(getValue(currData, idx, ss).y -
getValue(currData, idx - 1, ss).y) *
((eventX / layoutSize.value.width -
getValue(currData, idx - 1, ss).x) /
(getValue(currData, idx, ss).x -
getValue(currData, idx - 1, ss).x))) *
layoutSize.value.height;
}

setoriginalXYAccordingToPosition(
originalX,
originalY,
eventX / layoutSize.value.width,
curroriginalData
);
dotScale.value = withSpring(
1,
combineConfigs(springDefaultConfig, springConfig)
);

if (!android) {
positionX.value = positionXWithMargin(
eventX,
30,
layoutSize.value.width
);
positionY.value = currData.value[idx].y * layoutSize.value.height;
pathOpacity.value = withTiming(
0,
combineConfigs(timingFeedbackDefaultConfig, timingFeedbackConfig)
);
}
if (hapticsEnabledValue.value && !isStarted.value) {
impactHeavy();
}
isStarted.value = true;
positionX.value = eventX;
},
});

// @ts-ignore
const dotStyle = useAnimatedStyle(
() => ({
opacity: dotScale.value,
Expand Down Expand Up @@ -521,7 +558,7 @@ function ChartPath({
let toValue = currData.value;
let res;
let smoothing = 0;
let strategy = smoothingStrategy.value;
const strategy = smoothingStrategy.value;
if (progress.value !== 1) {
const numOfPoints = Math.round(
fromValue.length +
Expand Down
1 change: 1 addition & 0 deletions src/charts/linear/ChartPathProvider.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useState } from 'react';
import { useAnimatedStyle } from 'react-native-reanimated';

import ChartContext, { useGenerateValues } from '../../helpers/ChartContext';

export default function ChartPathProvider({ data: providedData, children }) {
Expand Down
1 change: 0 additions & 1 deletion src/helpers/useReactiveSharedValue.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect } from 'react';
// eslint-disable-next-line import/no-unresolved
import { useSharedValue } from 'react-native-reanimated';

export default function useReactiveSharedValue(prop, name) {
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/withReanimatedFallback.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { Text, TurboModuleRegistry } from 'react-native';

function ChartFallback() {
return <Text> Charts are not available without Reanimated 2</Text>;
return <Text>Charts are not available without Reanimated 2</Text>;
}

export default function withReanimatedFallback(
Expand Down
2 changes: 1 addition & 1 deletion src/interpolations/bSplineInterpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BSpline {
seqAt(dim) {
let points = this.points;
let margin = this.degree + 1;
return function (n) {
return function(n) {
if (n < margin) {
return points[0][dim];
} else if (points.length + margin <= n) {
Expand Down

0 comments on commit 6f57ff3

Please sign in to comment.