チャート関連の要件を記述するために最もよく使用されるチャート ライブラリは、 echartsです。
Web サイトでの echarts のパフォーマンスは非常に成熟しており、公式のソリューションはアプレット側に提供されていますが、RN には対応するサポートがありません。市場では、ほとんどの検索は依然として webview 実装の本質に基づいており、私は RN ベースのプログラムを好みます。結局、ネイティブ エクスペリエンスは Web よりも優れています。
後で、 @wuba/react-native-echarts が私のニーズを満たすのを見つけたので、試してみてください。結果は悪くありません。実装原理に興味のある方はこちら
これは、既にインターネット上で利用可能なローカル RN 開発環境を構築するプロセスなので、ここでは繰り返しません。 Googleで検索できます:)
試用だったので、expo を使用して、TestApp という rn プロジェクトを新しく初期化しました。
npx create-expo-app TestApp
コマンド ラインで ios および android アプリ パッケージを生成します。
エミュレーターの使用には iOS をお勧めします (証明書を一致させる必要はありません)。 Android では、実機に接続していました。
yarn android yarn ios
パッケージを生成すると、以下のようなアプリが既に電話にインストールされているため、成功しています。
yarn add @wuba/react-native-echarts echarts yarn add @shopify/react-native-skia yarn add react-native-svg
注: 既存のプロジェクトにインストールする場合は、インストールの完了後に新しいパッケージをビルドする必要があります。そうしないと、ネイティブの依存関係がないためにエラーが報告されます。
@wuba/react-native-echarts は 2 つのレンダリング モード (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> ); };
コードを書き、電話を振ってバンドル パッケージをリロードすると、エラーが報告されました。
ERROR Invariant Violation: requireNativeComponent: "SkiaDomView" が UIManager に見つかりませんでした。
私はそれをグーグルで調べたところ、バージョンのダウングレードが必要であると言われています。 expo バージョンに対応している必要があります。依存関係をインストールするときに同様のプロンプトが表示され、プロンプトされたバージョンをインストールすると問題ありません。
そのため、指示に従い、バージョンをダウングレードしました。
@shopify/[email protected] [email protected]
アプリを再構築した後、ロードされました。これは良かったです。 (ただし、Android はその点をカバーしています。画面幅は適応する必要があるようです。)
iOS | アンドロイド |
---|---|
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> ); };
この 2 つのモードの違いは、自分の目ではわかりません。
iOS | アンドロイド |
---|---|
これまでのところ、効果はかなりありましたが、インポートするためにたくさんのものを使用するたびに、それは私を悩ませました.
簡単にまとめましょう。
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 };
ラップしたら、複数のチャートを含むページを作成して、どのように機能するかを見てみましょう。折れ線グラフ、棒グラフ、円グラフを含む「ECデータ分析」のページです。以下は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 | アンドロイド |
---|---|
レンダリング後、iOS での操作は非常にスムーズですが、Android での操作は時折ラグが感じられます (私の携帯電話の調子が悪いからではありませんよね?...)。
Skiaモードを再試行中...
うーん、できるのですが、中国語がちゃんと表示できなかったり、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 モードはまだ使用できません。
上記は個人的な見解ですので、質問やコメントは以下からどうぞ!