D3 + React Native - значение точки данных не обновляется при изменении положения X - PullRequest
1 голос
/ 20 апреля 2020

Я пытаюсь создать собственную линейную диаграмму, следуя этому учебнику и этому . Все работает нормально, но когда я перетаскиваю палец на графике, я хочу показать значение в текущей позиции (приложение с линейными акциями на iOS или приложение Robinhood). Сначала он показывает значение, но его состояние c и не обновляется.

LineChart. js

const d3 = {
  scale,
  shape,
}
const height = 300
const { width } = Dimensions.get('window')
const verticalPadding = 30

export default function LineChart({ data = exampleData }) {
  const minX = minBy(data, el => moment(el.label, 'LT'))
  const maxX = maxBy(data, el => moment(el.label, 'LT'))
  const minY = minBy(data, el => el.value)
  const maxY = maxBy(data, el => el.value)
  const scaleX = scaleTime()
    .domain([moment(minX.label, 'LT'), moment(maxX.label, 'LT')])
    .range([0, width])
  const scaleY = scaleLinear()
    .domain([minY.value, maxY.value])
    .range([height - verticalPadding, verticalPadding])
  const line = d3.shape
    .line()
    .x(d => scaleX(moment(d.label, 'LT')))
    .y(d => scaleY(d.value))
    .curve(d3.shape.curveBasis)(data)

  return (
    <View style={styles.container}>
      <Svg {...{ width, height }}>
        <Path d={line} fill="transparent" stroke={GREEN} strokeWidth="2" />
      </Svg>
      <View style={{ ...StyleSheet.absoluteFill, width }}>
        <Cursor d={line} scaleY={scaleY} scaleX={scaleX} data={data} />
      </View>
    </View>
  )
}

Курсор. js

const { Value } = Animated
const { width } = Dimensions.get('window')

export default ({ d, scaleY, scaleX, data }) => {
  const translationX = new Value(0)
  const path = parsePath(d)
  const length = interpolate(translationX, {
    inputRange: [0, width],
    outputRange: [0, path.totalLength],
  })
  const { x, y } = getPointAtLength(path, length)
  const translateX = x
  const cursorX = sub(x, 4)
  const cursorY = sub(y, 4)
  const text = scaleY.invert(cursorX.__getValue())
  const onGestureEvent = event([
    {
      nativeEvent: {
        x: translationX,
      },
    },
  ])

  return (
    <PanGestureHandler onGestureEvent={onGestureEvent}>
      <Animated.View>
        <Animated.View style={{ transform: [{ translateX }], ...styles.label }}>
          <Animated.Text style={{ color: 'white' }}>{text}</Animated.Text>
        </Animated.View>
        <Animated.View style={[styles.line, { transform: [{ translateX }] }]} />
        <Animated.View
          style={[
            styles.cursor,
            { transform: [{ translateX: cursorX, translateY: cursorY }] },
          ]}
        />
      </Animated.View>
    </PanGestureHandler>
  )
}

Это результат кода выше:

current

Редактировать Вот ссылка на закуску: https://snack.expo.io/@clytras / заинтригованный трюфель

1 Ответ

1 голос
/ 24 апреля 2020

Вы используете React Native Reanimated call, чтобы получить обратный вызов при изменении значения translationX, а затем внутри него вы можете обновить текст, который он должен использовать setNativeProps и поскольку компонент <Text> не имеет text нативной поддержки, вам придется использовать <TextInput>, как в примере revolut-chart:

Курсор код компонента

// Changed imports
import React, { useEffect, useRef } from 'react';
import { Dimensions, TextInput } from 'react-native';
import Animated, { event, interpolate, sub, useCode, call } from 'react-native-reanimated';

...

export default function Cursor({ d, scaleY, scaleX, data }) {
  // Create a ref for the TextInput component
  const textRef = useRef();

  const translationX = new Value(0)
  const path = parsePath(d)
  const length = interpolate(translationX, {
    inputRange: [0, width],
    outputRange: [0, path.totalLength],
  })
  const { x, y } = getPointAtLength(path, length)
  const translateX = x
  const cursorX = sub(x, 4)
  const cursorY = sub(y, 4)
  // const text = scaleY.invert(cursorX.__getValue())
  const onGestureEvent = event([
    {
      nativeEvent: {
        x: translationX,
      },
    },
  ]);

  // Update text value using xValue = 0 when the component is mounted
  useEffect(() => {
    updateText(0);
  }, []);

  // Create reanimated code to get a translationX change callback
  useCode(() => {
    return call([translationX], (value) => {
      // On translationX value change update the text using the value
      updateText(value);
    })
  }, [translationX]);

  // Function to update the text based on current translationX value
  function updateText(xValue) {
    const { x, y } = getPointAtLength(path, xValue);
    const cursorX = sub(x, 4)
    const updated = scaleY.invert(cursorX.__getValue());

    // Use setNativeProps to update the TextInput component text prop
    textRef.current.setNativeProps({ text: `${updated.toFixed(5)}` })
  }

  return (
    <PanGestureHandler onGestureEvent={onGestureEvent}>
      <Animated.View>
        <Animated.View style={{ transform: [{ translateX }], ...styles.label }}>
          <TextInput ref={textRef} style={{ color: 'white' }}/>
        </Animated.View>
        <Animated.View style={[styles.line, { transform: [{ translateX }] }]} />
        <Animated.View
          style={[
            styles.cursor,
            { transform: [{ translateX: cursorX, translateY: cursorY }] },
          ]}
        />
      </Animated.View>
    </PanGestureHandler>
  )
}

Пример снимка экрана

RN D3 chart

Вы можете проверить обновленную закусочную Expo здесь: https://snack.expo.io/@clytras / заинтригован-трюфель

...