最近项目中开始使用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.
totalreturn 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,没数据时在容器中显示暂无数据,切换后无法显示图表
解决:
* 判断数据是否有值,没有值则进行处理
* 将实例的option先清空在setOption
* 将ECharts实例提取出来,不要每次都创建
* 具体代码可看上面代码块,不清楚可私信问我 // 默认用数据加载第一条渲染饼图,可能存在无数据情况,因此需要判断 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}`) })