ECharts在React+Antd用法及常見錯誤

最近項目中開始使用ECharts做圖表,在React+Antd中使用Echart還是遇到了很多坑,希望記錄下解決辦法可以幫助到大家。由於初次使用,很多代碼優化空間很大,希望大家能幫我提出,感恩~

最終效果

1、實現點擊折線圖上的點聯動兩個餅圖數據
2、處理ECharts餅圖數據爲0或者是空時餅圖消失問題

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

如何在react中使用ECharts

1、在render中添加div

<div id='echartLine' style={{ width: '100%', height: 300, margin: '24px 0' }} />
<div id='pieTotal' style={{ width: '100%', height: 300, margin: '24px 0' }} />
<div id='pieReject' style={{ width: '100%', height: 300, margin: '24px 0' }} />

2、 在對ECharts圖表進行init

	// 餅圖初始option
	const initPieOption = {
	  // tooltip: {
	  //   show: false,
	  // },
	  dataset: {
	    dimensions: ['desc', 'type', 'value'],
	    source: [{ desc: '', type: '', value: 1 }], // 當無數據時,給餅圖個初始數據
	  },
	  grid: {
	    left: '3%',
	    right: '3%',
	    bottom: '0%',
	    containLabel: true,
	  },
	  series: [
	    { type: 'pie',
	      radius: ['75%', '45%'],
	      stillShowZeroSum: false,
	      itemStyle: {
	        color: '#e7e7e7',
	      },
	      label: {
	        normal: {
	          show: true,
	          position: 'center',
	          formatter: function () {
	            var result = ''
	            result = '暫無數據'
	
	            return result
	          },
	          textStyle: {
	            fontSize: 16,
	            color: '#e2e2e2',
	          },
	        },
	      },
	    },
	  ],
	}
  // 初始化圖表
  initChart = () => {
    // const { trendList } = this.state

    // 折線圖
    var lineChart = echarts.init(document.getElementById('echartLine'))
    // 建議將ECharts圖表實例進行儲存,而不是每次數據變化都進行實例初始化
    this.setState({ lineChart })
    lineChart.setOption({
      title: {
        text: '趨勢',
      },
      tooltip: {
        trigger: 'axis',
        axisPointer: {
          type: 'cross',
        },
      },
      legend: {
        formatter: function (name) {
          switch (name) {
            case 'total':
              return '總量 ' + name
            case 'passCount':
              return '通過量 ' + name
            case 'rejectCount':
              return '拒絕量 ' + name
          }
        },
      },
      dataset: {
        // 這裏指定了維度名的順序,從而可以利用默認的維度到座標軸的映射。
        // 如果不指定 dimensions,也可以通過指定 series.encode 完成映射,參見後文。
        dimensions: ['date', 'total', 'passCount', 'rejectCount'],
        source: [],
      },
      xAxis: { type: 'category' },
      yAxis: {
        type: 'value',
      },
      grid: {
        left: '3%',
        right: '6%',
        bottom: '0%',
        containLabel: true,
      },
      series: [
        { type: 'line' },
        { type: 'line' },
        { type: 'line' },
      ],
    })
    // 餅圖總量
    var pieChartTotal = echarts.init(document.getElementById('pieTotal'))
    this.setState({ pieChartTotal })

    // 餅圖拒絕量
    var pieChartReject = echarts.init(document.getElementById('pieReject'))
    this.setState({ pieChartReject })

	// 使圖表自適應div大小,防止圖表溢出
    window.addEventListener('resize', function () {
      lineChart.resize()
      pieChartTotal.resize()
      pieChartReject.resize()
    })
  }

3、當數據變化重新渲染圖表

 // 獲取趨勢圖
  fetchBaseTrend = async () => {
    const res = await api.getBaseTrend()
    this.setState({
      trendList: res,
    }, () => {
      // 獲取到數據後再去調用渲染圖表函數
      this.generateChart()
    })
  }
  generateChart = () => {
    const { trendList, lineChart, pieChartTotal, pieChartReject } = this.state

    lineChart.setOption({
      dataset: {
        // 這裏指定了維度名的順序,從而可以利用默認的維度到座標軸的映射。
        // 如果不指定 dimensions,也可以通過指定 series.encode 完成映射,參見後文。
        dimensions: ['date', 'total', 'passCount', 'rejectCount'],
        source: trendList,
      },
    })
	// 由於我對餅圖無數據做了處理,所以每次setOption之前都要先執行clear(),防止之前setOption內的灰色背景屬性仍生效
    pieChartTotal.clear()
    pieChartReject.clear()

	// 默認用數據加載第一條渲染餅圖,可能存在無數據情況,因此需要判斷
    pieChartTotal.setOption(trendList[0] && !!trendList[0].totalRatio.length ? this.PieOption('total') : initPieOption)
    pieChartReject.setOption(trendList[0] && !!trendList[0].rejectRatio.length ? this.PieOption('reject') : initPieOption)
 }

4、如何點擊折線圖渲染餅圖

此部分代碼優化空間極大,但我改了很久還沒有找到更好的辦法,希望大家幫我提下意見,感恩~

  generateChart = () => {
  
    lineChart.on('click', function (event) {
      pieChartTotal.clear()

      pieChartTotal.setOption(event.data && !!event.data.totalRatio.length ? {
        dataset: {
          // 這裏指定了維度名的順序,從而可以利用默認的維度到座標軸的映射。
          // 如果不指定 dimensions,也可以通過指定 series.encode 完成映射,參見後文。
          dimensions: ['desc', 'type', 'value'],
          source: event.data && event.data.totalRatio,
        },
        legend: {
          orient: 'vertical',
          left: 70,
        },
        tooltip: {
          trigger: 'item',
          formatter: function (params) {
            var result = ''
            result = params.name + ' : ' + params.data.value + ' ( ' + params.percent + '% )'

            return result
          },
        },
        series: [
          { type: 'pie',
            radius: ['75%', '50%'],
            label: {
              normal: {
                show: true,
                position: 'center',
                color: '#4c4a4a',
                formatter: function (data) {
                  var result = ''
                  result = event.name + '\n' + '總量' + ' ' + event.data.total

                  return result
                },
                textStyle: {
                  fontSize: 16,
                  color: '#00c0ef',
                },
              },
            },
          },
        ],
      } : initPieOption)
      pieChartReject.clear()
      pieChartReject.setOption(event.data.rejectRatio && !!event.data.rejectRatio.length ? {
        dataset: {
          // 這裏指定了維度名的順序,從而可以利用默認的維度到座標軸的映射。
          // 如果不指定 dimensions,也可以通過指定 series.encode 完成映射,參見後文。
          dimensions: ['desc', 'type', 'value'],
          source: event.data && event.data.rejectRatio,
        },
        legend: {
          orient: 'vertical',
          right: 70,
        },
        series: [
          { type: 'pie',
            radius: ['75%', '45%'],
            label: {
              normal: {
                show: true,
                position: 'center',
                color: '#4c4a4a',
                formatter: function (data) {
                  var result = ''
                  result = event.name + '\n' + '拒絕量' + ' ' + event.data.rejectCount

                  return result
                },
                textStyle: {
                  fontSize: 16,
                  color: '#00c0ef',
                },
              },
            },
          },
        ],
      } : initPieOption)
    })
  }

4、trendList數據結構

[{
	"date": "2020-03-23",
	"total": 52,
	"passCount": 51,
	"rejectCount": 1,
	"totalRatio": [{
		"type": "text",
		"desc": "文本",
		"value": 27
	}, {
		"type": "picture",
		"desc": "圖片",
		"value": 25
	}],
	"rejectRatio": [{
		"type": "picture",
		"desc": "圖片",
		"value": 1
	}]
}, {
	"date": "2020-03-24",
	"total": 25,
	"passCount": 18,
	"rejectCount": 7,
	"totalRatio": [{
		"type": "picture",
		"desc": "圖片",
		"value": 15
	}, {
		"type": "text",
		"desc": "文本",
		"value": 10
	}],
	"rejectRatio": [{
		"type": "picture",
		"desc": "圖片",
		"value": 7
	}]
}]

ECharts常見問題

問題:Error: Component series.pie not exists. Load it first.
原因: 沒有引入pie的組件
解決:

import 'echarts/lib/chart/pie'

問題:ECharts3去掉了noDataLoadingOption,沒數據時在容器中顯示暫無數據,切換後無法顯示圖表
解決:

  1. 判斷數據是否有值,沒有值則進行處理
  2. 將實例的option先清空在setOption
  3. 將ECharts實例提取出來,不要每次都創建
  4. 具體代碼可看上面代碼塊,不清楚可私信問我
	// 默認用數據加載第一條渲染餅圖,可能存在無數據情況,因此需要判斷
    pieChartTotal.setOption(trendList[0] && !!trendList[0].totalRatio.length ? this.PieOption('total') : initPieOption)

問題:Label formatter 格式化內容,需要換行
原因: label是基於canvas的,不支持html,只支持換行\n
解決:

formatter: function (data) {
	var result = ''
	result = event.name + '\n' + '總量' + ' ' + event.data.total

	return result
},

問題:圖表溢出div,無法自適應
解決:

   window.addEventListener('resize', function () {
      lineChart.resize()
    })

問題:點擊事件多次綁定,導致回調多次觸發
解決:

  myChart.off('click')// 防止累計觸發
  myChart.on('click', function (event) {
     const url = reportTypesMap[item.key]['url']

     if (!url) return false
     const href = window.location.href.split('#')[0]
     window.open(`${href}#${url}?time=${event.name}`)
   })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章