La bibliothèque de graphiques la plus utilisée pour écrire des exigences liées aux graphiques est echarts .
Les performances des echarts sur le site Web sont assez matures et la solution officielle est fournie pour le côté applet, mais il n'y a pas de support correspondant dans RN. Sur le marché, la plupart des recherches sont toujours basées sur l'essence de la mise en œuvre de la vue Web, et je préfère le programme basé sur RN. Après tout, l'expérience native sera meilleure que le Web.
Plus tard, j'ai trouvé @wuba/react-native-echarts pour répondre à mes besoins, alors essayez-le ; les résultats ne sont pas mauvais. Pour les personnes intéressées par le principe de mise en œuvre, cliquez ici
Il s'agit du processus de création d'un environnement de développement RN local qui est déjà disponible sur Internet, je ne le reviendrai donc pas. Vous pouvez le rechercher sur Google :)
Comme il s'agissait d'un essai, j'ai utilisé l'expo pour initialiser un nouveau projet rn appelé TestApp.
npx create-expo-app TestApp
Générez des packages d'applications iOS et Android avec une ligne de commande.
iOS est recommandé d'utiliser l'émulateur (pas besoin de faire correspondre le certificat). Alors que sur Android, j'étais connecté à la vraie machine.
yarn android yarn ios
Après avoir généré le package, l'application comme celle ci-dessous est déjà installée sur le téléphone, ce qui signifie qu'elle a réussi.
yarn add @wuba/react-native-echarts echarts yarn add @shopify/react-native-skia yarn add react-native-svg
Remarque : si vous installez dans un projet existant, vous devez créer un nouveau package une fois l'installation terminée, sinon l'absence de dépendances natives signalera une erreur.
@wuba/react-native-echarts prend en charge deux modes de rendu (Skia et Svg) , essayez d'abord un graphique simple avec Skia.
Il est divisé en ces petites étapes:
Le code spécifique est le suivant :
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> ); };
Après avoir écrit le code, secoué le téléphone et rechargé le package groupé, une erreur a été signalée :
ERREUR Invariant Violation : requireNativeComponent : "SkiaDomView" n'a pas été trouvé dans UIManager.
Je l'ai googlé et il dit qu'il nécessite une rétrogradation de version . Cela devrait correspondre à la version expo, il y aura une invite similaire lors de l'installation de la dépendance, installez la version invitée et tout ira bien.
J'ai donc suivi les instructions et fait un downgrade de version :
@shopify/[email protected] [email protected]
Il s'est chargé après la reconstruction de l'application, ce qui était bien. (mais Android couvre le point, il semble que la largeur de l'écran devrait être adaptative.)
iOS | Android |
---|---|
Écrivez un graphique à barres de tri dynamique plus complexe avec le mode Svg et comparez Svg et Skia. Le code complet est ici 。
// ...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> ); };
Je ne vois pas la différence entre ces deux modes de mes propres yeux.
iOS | Android |
---|---|
Jusqu'ici l'effet était plutôt bon, mais à chaque fois que j'utilisais un tas de trucs à importer, ça me gênait.
Résumons simplement :
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 };
Une fois qu'il est emballé, écrivons une page avec plusieurs graphiques et voyons comment cela fonctionne. Voici une page pour "l'analyse des données de commerce électronique", comprenant un graphique linéaire, un graphique à barres et un graphique à secteurs. Vous trouverez ci-dessous le code principal écrit en mode svg, cliquez ici pour le code détaillé.
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 | Android |
---|---|
Après le rendu, l'interaction sur iOS est très fluide, tandis que l'interaction sur Android semble parfois lag (pas parce que mon téléphone est trop mauvais, non ?...)
Essayer à nouveau le mode Skia…
Eh bien, même si c'est possible, il semble que le chinois ne puisse pas être affiché correctement, le chinois Android ne s'affiche pas et iOS est un gâchis de code.
Après avoir lu la documentation, skia ne prend actuellement pas en charge le chinois du côté Android,
Nous pouvons afficher le chinois sur iOS en définissant la police sur "PingFang SC", par exemple :
const option = { title: { text: '我是中文', textStyle: { fontFamily: 'PingFang SC', // setting the font type }, }, };
Mais chaque endroit qui affiche le chinois doit définir la police ... cela ou utiliser Svg en premier, je suis paresseux.
Après l'avoir utilisé pendant un certain temps, j'ai résumé ce qui suit:
En termes de support, @wuba/react-native-echarts prend en charge tous les types de graphiques, à l'exception des séries GL et des graphiques cartographiques qui ne sont pas encore pris en charge, ce qui est largement suffisant pour les activités quotidiennes. Le code pour implémenter les différents graphiques dans echarts se trouve dans taro-playground .
Interaction, iOS est très fluide, Android parfois il y a des cas de chutes de trame.
Performances : les performances sont officiellement signalées comme meilleures que les autres solutions.
Prise en charge du chinois, le mode Svg prend en charge le chinois, mais le mode Skia n'est pas encore disponible.
Ce qui précède n'est qu'un point de vue personnel, toutes les questions et commentaires sont les bienvenus ci-dessous !