【React-Native中】使用antv/f2实现饼图和折线图

1,688 阅读2分钟

最近在写一个RN-app项目,需要用到折线图和饼图,原本是使用第三方echarts,使用发现折线图无法在安卓机显示,饼图虽能显示,却不能更新数据,最终决定改用阿里的antv/f2可视化库。

@TOC


前言

思路:使用react-native-webview加载本地html文件。

提示:以下是本篇文章正文内容,下面案例可供参考

一、前提准备

安装react-native-webview

yarn add react-native-webview -s

cd ios/

pod install

二、代码步骤

目录结构

1.新建f2chart.html

文件地址

在组件文件夹下复制粘贴该html文件,同时,需配置android,手动将次文件放置在android/app/src/main/assets/f2chart.html ,如果没有该文件夹,手动创建一个

2.charts.js加载js脚本

代码如下(示例):

import React, { PureComponent, createRef } from 'react';
import { StyleSheet, Platform } from 'react-native';
import { WebView as RNWebView } from 'react-native-webview';
import renderChart, { changeChartData } from './renderChart';

const source = Platform.select({
    // eslint-disable-next-line global-require
    ios: require('./f2chart.html'),
    android: { uri: 'file:///android_asset/f2chart.html' },
});

export default class Chart extends PureComponent {

    constructor(props) {
        super(props);
        this.chart = createRef();
    }

    // eslint-disable-next-line react/no-deprecated
    componentDidUpdate(prevProps, prevState) {
        const { data } = this.props;
        if (JSON.stringify(data) !== JSON.stringify(prevProps.data)) {
            this.reload();
            this.chart.current.injectJavaScript(changeChartData(prevProps));
        }
    }



    update = data => { };

    onMessage = event => {
        const {
            nativeEvent: { data },
        } = event;
        const { onChange } = this.props;
        const tooltip = JSON.parse(data);
        onChange(tooltip);
    };

    reload = () => {
        this.chart.current.reload();
    };

    onLoadEnd = () => {
        setTimeout(() => {
            this.chart.current.injectJavaScript(renderChart(this.props));
        }, 10);
    };

    render() {
        const { data, ...props } = this.props;
        return (
            <RNWebView
                scrollEnabled={false}
                allowFileAccess={true}
                javaScriptEnabled
                ref={this.chart}
                style={styles.webView}
                injectedJavaScript={renderChart(this.props)}
                source={source}
                onLoadEnd={this.onLoadEnd}
                originWhitelist={['*']}
                onMessage={this.onMessage}
                {...props}
            />
        );
    }
}

const styles = StyleSheet.create({
    webView: {
        flex: 1,
        backgroundColor: 'transparent',
    },
});

3.renderChart.js

// 从数组中获取最大的值
const getMaxValue = list => {
  const nums = list.map(v => v.value);
  return Math.max.apply(null, nums);
};

function sum(arr) {
  return eval(arr.join("+"));
};

export const changeChartData = props => {
  const { data = [] } = props;
  const chartData =
    data.map(c => {
      return {
        ...c,
        date: c.date,
      };
    })
  return `chart && chart.changeData(${JSON.stringify(chartData)})`;
};

export default function renderChart(props) {
  const { data = [], option } = props;
  const chartData = data.map(c => {
    return {
      ...c,
      date: c.date, // 可在此处做时间格式处理
    };
  })


  const lastData = chartData[chartData.length - 1];
  const max = getMaxValue(chartData)

  //简单做了折线图和饼图区分 其他类型需自行修改
  if (option.type !== 'pie') {
    const script = `
  (function(){
    const chart = new F2.Chart({
      id: 'chart',
      pixelRatio: window.devicePixelRatio,
      padding: [ 'auto', 'auto',60, 'auto' ]
    });
    
    chart.source(${JSON.stringify(chartData)}, {
      value: {
        tickCount: 5,
        min: 0,
        // max:${JSON.stringify(max)}*1.2,
      },
      date: {
        type: 'cat',
        range: [0.02, 0.98],
        mark:'MM-DD',
        tickCount: 7,
      }
    });

    chart.legend(${option.legend ? true : false},{
      position: 'bottom',
      offsetY: ${option.series.length > 5}?0:-12,
      align:'center',
      itemWidth:null,
      itemMarginBottom:8,
      marker(x, y, r, ctx) {
        ctx.lineWidth = 2;
        ctx.strokeStyle = ctx.fillStyle;
        ctx.moveTo(x - r - 3, y);
        ctx.lineTo(x + r + 3, y);
        ctx.stroke();
        ctx.arc(x, y, r, 0, Math.PI * 2, false);
        ctx.fill();
      }
    });

    chart.tooltip({
      showCrosshairs:true,
    });

    //移动端手势: pinch 和 pan,在电脑上是不能模拟的

    chart.scrollBar({
      mode:'x',
      xStyle:{
        backgroundColor: '#f5f5f5', 
        fillerColor: '${option.datazoom.color}'||'#5fc48d', 
        size: 4, 
        lineCap: 'round', 
        offsetX: 0 , 
        offsetY: ${!option.legend}?-18:0
      },
    });
    chart.interaction('pan', {
      step:1,
      speed:10,
      panThreshold:1,
      pressThreshold:1,
    });

    chart.interaction('pinch', {
      background:'#000',
      mode: 'x', // 图表平移的方向,默认为 'x'
      minScale: 0.1, // 缩小的最小倍数
      maxScale: 4, // 放大的最大倍数
      pressThreshold: 9, // hammer.js 设置,用于设置触发 press 事件的设置
      pressTime: 251 // hammer.js 设置,用于设置触发 press 事件的最小时间差
    });
    
   
    chart.axis('value', {
      line:F2.Global._defaultAxis.line,
      grid:null,
      label: function label(text, index, total) {
        const textCfg = {
          text
        };
        return textCfg;
      }
    });
    chart.axis('date', {
      label: function label(text, index, total) {
        const textCfg = {
        };
        // if (index === 0) {
        //   textCfg.textAlign = 'left';
        // } else if (index === total - 1) {
        //   textCfg.textAlign = 'right';
        // }
        return textCfg;
      }
    });
    chart.line({
      sortable: false
    }).position('date*value')
    .shape('smooth')
    .color('type',${JSON.stringify(option.colors)})

    chart.render();
 
  })();
  `;
    return script
  } else {
    let total = sum(data.map(item => { return item.value }))
    //是否显示每块区域的名称及比例
    const pieLabel = option?.pieLabel ? ` 
    chart.pieLabel({
      label1(data, color) {
        return {
          text: data.name+' '+ (data.value/${total}*100).toFixed(2)+'%', // 文本内容
          fill: color // 文本颜色
        };
     }
    });
    `: ``
    const pieScript = `
(function(){
    const chart = new F2.Chart({
      id: 'chart',
      pixelRatio: window.devicePixelRatio,
      padding:0
    });
      chart.source(${JSON.stringify(data)},{
        name:{},
        value:{}
      });
      chart.coord('polar', {
        transposed: true,
        innerRadius: ${option?.series[0]?.radius[0]},
        radius: ${option?.series[0]?.radius[1]},  
        
      });
      chart.axis(false);
      chart.legend(false);
      chart.tooltip(false);

       ${pieLabel}
      //饼图选中
      // chart.interaction('pie-select');
      
      chart
        .interval()
        .position('1*value')
        .color('name', ${JSON.stringify(option.colors)})
        .adjust('stack')
        .style({
          lineWidth: 1,
          stroke: '#fff'
        });
      chart.render();
    })();
    `
    return pieScript;
  }

}

具体配置:官方API

4.使用

import F2Chart from './F2Chart'

<F2Chart
   option={pieOption}
   style={{ height: scaleHeight(250) }}
/>

效果图

在这里插入图片描述

相关代码内容可查看:f2chartRNDemo