paint-brush
A melhor maneira de fazer gráficos nativos de reação em 2023por@zhiqingchen
4,000 leituras
4,000 leituras

A melhor maneira de fazer gráficos nativos de reação em 2023

por zhiqingchen13m2023/03/30
Read on Terminal Reader

Muito longo; Para ler

A biblioteca de gráficos mais usada para escrever requisitos relacionados a gráficos é **echarts**. O desempenho de echarts no site é bastante maduro e a solução oficial é fornecida para o lado do applet, mas não há suporte correspondente no RN. No mercado, a maior parte da pesquisa ainda é baseada na essência da implementação do webview, e eu prefiro o programa baseado em RN.
featured image - A melhor maneira de fazer gráficos nativos de reação em 2023
zhiqingchen HackerNoon profile picture
0-item
1-item

A biblioteca de gráficos mais usada para escrever requisitos relacionados a gráficos é echarts .


O desempenho dos echarts no site é bastante maduro e a solução oficial é fornecida para o lado do applet, mas não há suporte correspondente no RN. No mercado, a maior parte da pesquisa ainda é baseada na essência da implementação do webview, e eu prefiro o programa baseado em RN. Afinal, a experiência nativa será melhor que a web.


Mais tarde, encontrei @wuba/react-native-echarts para atender às minhas necessidades, então experimente; os resultados não são ruins. Para os interessados no princípio de implementação, clique aqui

Pontas

  • Se você já possui um pacote de APP, pode ignorar o processo de empacotamento anterior e começar diretamente na etapa 4.
  • O código completo para o teste está no GitHub em: https://github.com/iambool/TestApp

As etapas para usá-lo são as seguintes

Etapa 1. Configuração do ambiente de desenvolvimento

Este é o processo de construção de um ambiente de desenvolvimento local do RN que já está disponível na Internet, então não vou falar sobre isso novamente. Você pode procurar no Google :)

Passo 2. Criando um projeto RN

Como era um teste, usei a exposição para inicializar novamente um projeto rn chamado TestApp.

 npx create-expo-app TestApp 

criar TestApp

Etapa 3. Construindo um aplicativo em dispositivos móveis

Gere pacotes de aplicativos ios e android com uma linha de comando.


iOS é recomendado para usar o emulador (não há necessidade de corresponder ao certificado). Enquanto no Android, eu estava conectado à máquina real.


 yarn android yarn ios


Depois de gerar o pacote, o aplicativo como o abaixo já está instalado no telefone, o que significa que foi bem-sucedido.

foto

Etapa 4. Instale as dependências relacionadas

 yarn add @wuba/react-native-echarts echarts yarn add @shopify/react-native-skia yarn add react-native-svg

Observação: se você estiver instalando em um projeto existente, deverá criar um novo pacote após a conclusão da instalação, caso contrário, a falta de dependências nativas informará um erro.

Etapa 5. Experimente o modelo Skia

@wuba/react-native-echarts suporta dois modos de renderização (Skia e Svg) , experimente primeiro um gráfico simples com o Skia.


É dividido nestas pequenas etapas:


  • Introduzir echarts, componentes de gráfico e outras dependências.
  • Registrando componentes do gráfico.
  • Crie uma instância de gráfico e defina uma opção.
  • Destruição sincronizada de instâncias do gráfico quando a página é destruída.


O código específico é o seguinte:

 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> ); };


Depois de escrever o código, agitar o telefone e recarregar o pacote, um erro foi relatado:

ERRO Violação invariável: requireNativeComponent: "SkiaDomView" não foi encontrado no UIManager.


Pesquisei no Google e diz que requer um downgrade de versão . Deve corresponder à versão expo, haverá um prompt semelhante ao instalar a dependência, instale a versão solicitada e tudo ficará bem.

aviso

Então segui as instruções e fiz um downgrade de versão:

 @shopify/[email protected] [email protected]

Ele carregou depois de reconstruir o aplicativo, o que foi legal. (mas o Android cobre o ponto, parece que a largura da tela deve ser adaptável.)

iOS

Android

iOS

Android

Etapa 6. Experimente o modelo Svg

Escreva um gráfico de barras de classificação dinâmica mais complexo com o modo Svg e compare Svg e Skia. O código completo está aqui .

 // ...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> ); };


Não consigo ver a diferença entre esses dois modos com meus próprios olhos.

iOS

Android

foto

foto

Etapa 7. Envolvendo os componentes do gráfico

Até agora o efeito foi muito bom, mas toda vez que eu usava um monte de coisas para importar, me incomodava.


Vamos resumir de forma simples:

 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 };

Etapa 8. Usando vários gráficos

Depois de agrupado, vamos escrever uma página com vários gráficos e ver como funciona. Aqui está uma página para “análise de dados de comércio eletrônico”, incluindo um gráfico de linha, gráfico de barras e gráfico de pizza. Abaixo está o código principal escrito com o modo svg, clique aqui para obter o código detalhado.


 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> ); };


Recarregue o pacote e veja o resultado

iOS

Android

foto

foto

Após a renderização, a interação no iOS é muito suave, enquanto a interação no Android parece ocasionalmente lenta (não porque meu telefone seja muito ruim, certo?...)


Tentando o modo Skia novamente…

foto

Bem, embora possa, parece que o chinês não pode ser exibido corretamente, o chinês do Android não é exibido e o iOS é uma bagunça de código.


Depois de ler a documentação, skia atualmente não suporta chinês no lado do Android,


Podemos exibir chinês no iOS definindo a fonte como 'PingFang SC', por exemplo:

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

Mas todo lugar que exibe chinês tem que definir a fonte ... isso ou usar Svg primeiro, sou preguiçoso.

Resumo

Depois de usá-lo por um tempo, resumi o seguinte:

  • Em termos de suporte, @wuba/react-native-echarts oferece suporte a todos os tipos de gráficos, exceto a série GL e gráficos de mapas que ainda não são suportados, o que é suficiente para os negócios diários. O código para implementar os vários gráficos em echarts pode ser encontrado em taro-playground .

  • Interação, iOS é muito suave, Android às vezes há casos de quedas de quadros.

  • Desempenho: O desempenho é oficialmente relatado como melhor do que outras soluções.

    • Eu tentei, uma quantidade não muito grande de dados não terá problemas, mas quando a quantidade de dados é muito grande (como desenhar uma grande quantidade de mapa de calor de dados), a velocidade de renderização diminuiu significativamente, o que é um ponto esperando que o funcionário otimize.
    • Além disso, se houver muitos gráficos na página, a velocidade de carregamento será lenta ao depurar na máquina real, por isso é recomendável usar o simulador primeiro.
  • Suporte chinês, o modo Svg suporta chinês, mas o modo Skia ainda não está disponível.


O acima é apenas uma visão pessoal, quaisquer perguntas e comentários são bem-vindos abaixo!