Лучший способ сделать React Native Charting в 2023 году

Лучший способ сделать React Native Charting в 2023 году

31 марта 2023 г.

Наиболее часто используемой библиотекой диаграмм для написания требований к диаграммам является echarts.

Производительность echarts на сайте вполне зрелая, и на стороне апплета предусмотрено официальное решение, но соответствующей поддержки в RN нет. На рынке большая часть поиска по-прежнему основывается на сути реализации webview, и я предпочитаю программу на базе RN. В конце концов, собственный опыт будет лучше, чем в Интернете.

Позже я нашел @wuba/react-native-echarts для удовлетворения своих потребностей. , так что попробуйте; результаты не плохие. Для тех, кто интересуется принципом реализации, нажмите здесь

Советы

  • Если у вас уже есть пакет APP, вы можете пропустить предыдущий процесс упаковки и начать сразу с шага 4.
  • Полный код пробной версии находится на GitHub по адресу: https://github.com/iambool/TestApp

Чтобы его использовать, выполните следующие действия

Шаг 1. Настройка среды разработки

Это процесс создания локальной среды разработки RN, которая уже доступна в Интернете, поэтому я не буду повторяться. Вы можете найти его в Google :)

Шаг 2. Создание проекта RN

Поскольку это была пробная версия, я использовал выставку для новой инициализации проекта rn под названием TestApp.

npx create-expo-app TestApp

create TestApp

Шаг 3. Создание приложения для мобильных устройств

Создавайте пакеты приложений для iOS и Android с помощью командной строки.

Для iOS рекомендуется использовать эмулятор (нет необходимости соответствовать сертификату). На Android я был подключен к настоящей машине.

yarn android
yarn ios

После создания пакета приложение, подобное приведенному ниже, уже установлено на телефоне, что означает, что оно выполнено успешно.

picture

Шаг 4. Установите связанные зависимости

yarn add @wuba/react-native-echarts echarts
yarn add @shopify/react-native-skia
yarn add react-native-svg
<цитата>

Примечание: если вы устанавливаете в существующий проект, вам необходимо создать новый пакет после завершения установки, иначе отсутствие собственных зависимостей сообщит об ошибке.

Шаг 5. Попробуйте модель Skia

@wuba/react-native-echarts поддерживает два режима рендеринга (Skia и Svg). Сначала попробуйте простую диаграмму со Skia.

Он разделен на следующие небольшие этапы:

* Внедрение диаграмм, компонентов диаграмм и других зависимостей. * Регистрация компонентов диаграммы. * Создайте экземпляр диаграммы и установите параметр. * Синхронное уничтожение экземпляров диаграммы при уничтожении страницы.

Конкретный код выглядит следующим образом:

import { useRef, useEffect } from 'react';
import { View } from 'react-native';
/**
 * 1. Import the echarts dependency, this example first tries the line chart
 */
import * as echarts from 'echarts/core';
import { LineChart } from 'echarts/charts';
import { GridComponent } from 'echarts/components';
import { SVGRenderer, SkiaChart } from '@wuba/react-native-echarts';

/**
 * 2. Register the required components
 * SVGRenderer: it is required to register
 * LineChart: because we want to show the line chart, we have to import LineChart
 *      - If you don't know which components to import, just look at the error report and add whatever the error says is missing
 * GridComponent: This is the prompt when the error is reported, and then I added the, ha ha
 */
echarts.use([SVGRenderer, LineChart, GridComponent]);

export default () => {
  const skiaRef = useRef(null); // Ref for saving chart instances
  useEffect(() => {
    /**
     * 3. chart option
     */
    const option = {
      xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
      },
      yAxis: {
        type: 'value',
      },
      series: [
        {
          data: [150, 230, 224, 218, 135, 147, 260],
          type: 'line',
        },
      ],
    };
    let chart;
    if (skiaRef.current) {
      /**
       * 4. Initialize the chart, specifying the lower width and height
       */
      chart = echarts.init(skiaRef.current, 'light', {
        renderer: 'svg',
        width: 400,
        height: 400,
      });
      chart.setOption(option);
    }
    /**
     * 5. To destroy the chart instance after the page is closed
     */
    return () => chart?.dispose();
  }, []);
  return (
    <View className='index'>
      <SkiaChart ref={skiaRef} />
    </View>
  );
};

После написания кода, встряхивания телефона и перезагрузки бандла было сообщено об ошибке:

<цитата>

ОШИБКА Нарушение инварианта: requireNativeComponent: "SkiaDomView" не найден в UIManager.

Я погуглил, и там написано, что требуется понижение версии. Она должна соответствовать expo-версии, при установке зависимости будет похожее приглашение, установите предложенную версию и все будет в порядке.

warning

Поэтому я следовал инструкциям и понизил версию:

@shopify/react-native-skia@0.1.157
react-native-svg@13.4.0

Он загрузился после пересборки приложения, что было приятно. (но Android скрывает суть, кажется, что ширина экрана должна быть адаптивной.)

| iOS | Андроид | |----|----| | iOS | Android |

Шаг 6. Попробуйте модель Svg

Создайте более сложную линейчатую диаграмму с динамической сортировкой в ​​режиме Svg и сравните Svg и Skia. Полный код находится здесь

// ...Some unimportant code is omitted here

// Register the required components, such as BarChart and LegendComponent
echarts.use([SVGRenderer, BarChart, LegendComponent, GridComponent]);

export default () => {
  const skiaRef = useRef(null);
  const svgRef = useRef(null);

  useEffect(() => {
    // Skia mode
    const skiaChartData = getData(); // Generate chart bar data
    let skiaChart;
    let skiaInter;
    if (skiaRef.current) {
      skiaChart = echarts.init(skiaRef.current, 'light', {
        renderer: 'svg',
        width: 300,
        height: 300,
      });
      skiaChart.setOption(getDefaultOption(skiaChartData));
      setTimeout(function () {
        run(skiaChart, skiaChartData);
      }, 0);
      skiaInter = setInterval(function () {
        run(skiaChart, skiaChartData);
      }, 3000);
    }

    // Svg mode
    const svgChartData = getData();
    let svgChart;
    let svgInter;
    if (svgRef.current) {
      svgChart = echarts.init(svgRef.current, 'light', {
        renderer: 'svg',
        width: 300,
        height: 300,
      });
      svgChart.setOption(getDefaultOption(svgChartData));
      setTimeout(function () {
        run(svgChart, svgChartData);
      }, 0);
      svgInter = setInterval(function () {
        run(svgChart, svgChartData);
      }, 3000);
    }

    return () => {
      skiaChart?.dispose();
      svgChart?.dispose();
      // The timer has to be cleaned up, otherwise it will still run after exiting the page
      clearInterval(skiaInter);
      clearInterval(svgInter);
    };
  }, []);
  return (
    <View>
      <Text>skia</Text>
      <SkiaChart ref={skiaRef} />
      <Text>svg</Text>
      <SvgChart ref={svgRef} />
    </View>
  );
};

Я своими глазами не вижу разницы между этими двумя режимами.

| iOS | Андроид | |----|----| | picture | picture |

Шаг 7. Обтекание компонентов диаграммы

До сих пор эффект был довольно хорошим, но каждый раз, когда я использовал кучу вещей для импорта, это меня беспокоило.

Давайте закончим просто:

import { useRef, useEffect } from 'react';
import * as echarts from 'echarts/core';
import { BarChart, LineChart, PieChart } from 'echarts/charts';
import {
  DataZoomComponent,
  GridComponent,
  LegendComponent,
  TitleComponent,
  ToolboxComponent,
  TooltipComponent,
} from 'echarts/components';
import {
  SVGRenderer,
  SvgChart as _SvgChart,
  SkiaChart as _SkiaChart,
} from '@wuba/react-native-echarts';
import { Dimensions } from 'react-native';

// Register the required components
echarts.use([
  DataZoomComponent,
  SVGRenderer,
  BarChart,
  GridComponent,
  LegendComponent,
  ToolboxComponent,
  TooltipComponent,
  TitleComponent,
  PieChart,
  LineChart,
]);

// Default width and height of the chart
const CHART_WIDTH = Dimensions.get('screen').width; // Default with the phone screen width
const CHART_HEIGHT = 300;

const Chart = ({
  option,
  onInit,
  width = CHART_WIDTH,
  height = CHART_HEIGHT,
  ChartComponent,
}) => {
  const chartRef = useRef(null);

  useEffect(() => {
    let chart;
    if (chartRef.current) {
      chart = echarts.init(chartRef.current, 'light', {
        renderer: 'svg',
        width,
        height,
      });
      option && chart.setOption(option);
      onInit?.(chart);
    }
    return () => chart?.dispose();
  }, [option]);
  return <ChartComponent ref={chartRef} />;
};

const SkiaChart = (props) => <Chart {...props} ChartComponent={_SkiaChart} />;
const SvgChart = (props) => <Chart {...props} ChartComponent={_SvgChart} />;
// Just export these two guys
export { SkiaChart, SvgChart };

Шаг 8. Использование нескольких диаграмм

После завершения давайте напишем страницу с несколькими диаграммами и посмотрим, как это работает. Вот страница для «анализа данных электронной коммерции», включая линейную диаграмму, гистограмму и круговую диаграмму. Ниже приведен основной код, написанный в режиме svg. Нажмите здесь для подробного кода.

import { SkiaChart } from '../../components/Chart';
import { ScrollView, Text, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { useCallback, useEffect, useState } from 'react';
import {
  defaultActual,
  lineOption,
  salesStatus,
  salesVolume,
  userAnaly,
  getLineData,
} from './contants';
import styles from './styles';
// Turn on chart loading
const showChartLoading = (chart) =>
  chart.showLoading('default', {
    maskColor: '#305d9e',
  });
// Close chart loading
const hideChartLoading = (chart) => chart.hideLoading();

export default () => {
  const [actual, setActual] = useState(defaultActual); // Recording real-time data

  useEffect(() => {
    // Assuming a recurring request for data
    const interv = setInterval(() => {
      const newActual = [];
      for (let it of actual) {
        newActual.push({
          ...it,
          num: it.num + Math.floor((Math.random() * it.num) / 100),
        });
      }
      setActual(newActual);
    }, 200);
    return () => clearInterval(interv);
  }, [actual]);

  const onInitLineChart = useCallback((myChart) => {
    showChartLoading(myChart);
    // Simulation of data requests
    setTimeout(() => {
      myChart.setOption({
        series: getLineData,
      });
      hideChartLoading(myChart);
    }, 1000);
  }, []);

  const onInitUserChart = useCallback((myChart) => {
    // Simulate data request, similar to onInitLineChart
  }, []);
  const onInitSaleChart = useCallback((myChart) => {
    // Simulate data request, similar to onInitLineChart
  }, []);
  const onInitStatusChart = useCallback((myChart) => {
    // Simulate data request, similar to onInitLineChart
  }, []);

  const chartList = [
    ['订单走势', lineOption, onInitLineChart],
    ['用户统计', userAnaly, onInitUserChart],
    ['各品类销售统计', salesVolume, onInitSaleChart],
    ['订单状态统计', salesStatus, onInitStatusChart],
  ];

  return (
    <ScrollView style={styles.index}>
      <StatusBar style='light' />
      <View>
        <View style={styles.index_panel_header}>
          <Text style={styles.index_panel_title}>实时数据</Text>
        </View>
        <View style={styles.index_panel_content}>
          {actual.map(({ title, num, unit }) => (
            <View key={title} style={styles.sale_item}>
              <View style={styles.sale_item_cell}>
                <Text style={styles.sale_item_text}>{title}</Text>
              </View>
              <View style={[styles.sale_item_cell, styles.num]}>
                <Text style={styles.sale_item_num}>{num}</Text>
              </View>
              <View style={[styles.sale_item_cell, styles.unit]}>
                <Text style={styles.sale_item_text}>{unit}</Text>
              </View>
            </View>
          ))}
        </View>
      </View>
      {chartList.map(([title, data, callback]) => (
        <View key={title}>
          <View style={styles.index_panel_header}>
            <Text style={styles.index_panel_title}>{title}</Text>
          </View>
          <View style={styles.index_panel_content}>
            <SkiaChart option={data} onInit={callback} />
          </View>
        </View>
      ))}
    </ScrollView>
  );
};

Перезагрузите пакет и посмотрите результат

| iOS | Андроид | |----|----| | picture | picture |

После рендеринга взаимодействие на iOS происходит очень плавно, в то время как взаимодействие на Android иногда кажется медленным (не потому, что мой телефон слишком плохой, верно?...)

Повторная попытка режима Skia…

picture

Ну, хотя может, похоже, что китайский язык не может отображаться должным образом, китайский язык Android не отображается, а iOS представляет собой беспорядок кода.

Прочитав документацию, skia в настоящее время не поддерживает китайский язык на стороне Android,

Мы можем отображать китайский язык на iOS, установив шрифт «PingFang SC», например:

const option = {
  title: {
    text: '我是中文',
    textStyle: {
      fontFamily: 'PingFang SC', // setting the font type
    },
  },
};

Но в каждом месте, где отображается китайский язык, нужно установить шрифт... либо сначала использовать Svg, мне лень.

Обзор

После некоторого использования я подытожил следующее:

  • Что касается поддержки, @wuba/react-native-echarts поддерживает все типы диаграмм, кроме серий GL и картографических диаграмм, которые еще не поддерживаются, чего вполне достаточно для повседневного бизнеса. Код для реализации различных диаграмм в echarts можно найти в taro-playground.
  • Взаимодействие на iOS очень плавное, на Android иногда случаются пропадания кадров.
  • Производительность. Официально заявлено, что производительность выше, чем у других решений.
  • Я попробовал, не очень большой объем данных не вызовет проблем, но когда объем данных слишком велик (например, рисование большого объема данных тепловой карты), скорость рендеринга значительно снижается, и это вопрос, ожидающий оптимизации чиновника.
  • Кроме того, если на странице много диаграмм, скорость загрузки будет низкой при отладке на реальном компьютере, поэтому рекомендуется сначала использовать симулятор.
  • Поддержка китайского языка, режим Svg поддерживает китайский язык, но режим Skia пока недоступен.

Вышеупомянутое является только личным мнением, любые вопросы и комментарии приветствуются ниже!


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE