Echart 复合饼图
要实现如Excel中一样的复合饼图,但发现echarts中并没有相关样例,故根据配置项自行实现
实现思路
- 利用echarts中的嵌套环形图进行修改
1.1. 将中间内圆进行平移,构成复合饼图的详细子图,通过media
属性实现
media: [
{
query: { minAspectRatio: 1 },
option: {
series: [
{ center: ['70%', '50%'] },
{ center: ['30%', '50%'] },
]
}
}
],
实现效果:
2. 接下来就是要为右边的圆以及左边对应的扇形增加连线,这里用到的markline
属性,可以在图中增加自定义的线条.
线的起点在左边扇形边界,线的终点在圆的边上。
因为比较懒,懒得计算,所以右边圆上的终点直接写死了。
所以接下来的重点在于获取到左边扇形的上下边界坐标点:
- 首先需要获取到环形图的属性,需要在图初次完成渲染时获取到图的信息,找到环形图的
_itemLayouts
属性,该属性拥有每个扇形圆心坐标,半径值,开始角度,结束角度 - 根据获得的圆心坐标(
cx
,cy
),半径值(r
),开始角度(startAngle
),结束角度 (endAngle
)计算扇形的边界坐标,然后更新markLine的起始点就可以得到从扇形到圆的连线- 计算方式:
开始边界x坐标(x1)= 圆心x坐标 + cos(开始角度) * 半径值
开始边界y坐标(y1)= 圆心y坐标 + sin(开始角度) * 半径值
结束边界x坐标(x2)= 圆心x坐标 + cos(结束角度) * 半径值
结束边界y坐标(y2)= 圆心y坐标 + sin(结束角度) * 半径值
- 得到(x1, y1),(x2, y2)之后,将值以及之前定好的终点圆的边界坐标写到配置项
markLine
中,就可以得到复合饼图
- 计算方式:
- 最终效果
代码(可复制到codeSandbox中运行)
import "./styles.css";
import React, {useRef, useMemo, useEffect, useState} from 'react';
import ReactEcharts from 'echarts-for-react';
export default function App() {
const [chartView, setChartView] = useState(null);
const myCharts = useRef(null);
useEffect(()=>{
window.addEventListener('resize', chartResize);
return () =>{window.removeEventListener('resize', chartResize);}
}, [])
const chartResize = () => {
setChartView(null);
myCharts && myCharts.current.getEchartsInstance().resize();
}
const chartOptions = useMemo(() => {
console.log('111',chartView)
let changePos = false;
let pos = {};
if (chartView) {
changePos = true;
let tmp = chartView[1]._data._itemLayouts[0];
pos.startTop = {x: tmp.cx + Math.cos(tmp.startAngle) * tmp.r, y: tmp.cy + Math.sin(tmp.startAngle) * tmp.r}
pos.startBootom = {x: tmp.cx + Math.cos(tmp.endAngle) * tmp.r, y: tmp.cy + Math.sin(tmp.endAngle) * tmp.r}
}
return {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
data: [
'Direct',
'Marketing',
'Search Engine',
'Email',
'Baidu',
]
},
series: [
{
name: 'Access From',
type: 'pie',
selectedMode: 'single',
radius: [0, '30%'],
label: {
show: false,
position: 'inner',
fontSize: 14
},
labelLine: {
show: false
},
data: [
{ value: 1548, name: 'Search Engine' },
{ value: 775, name: 'Direct' },
{ value: 679, name: 'Marketing' }
]
},
{
name: 'Access From',
type: 'pie',
radius: ['45%', '60%'],
label: {
show: false
},
labelLine: {
length: 30
},
startAngle: 60,
data: [
{ value: 1048, name: 'Baidu' },
{ value: 2046, name: 'Email' },
],
markLine:{
silent: true,
symbol: 'none',
data: [
[
{x: changePos ? pos.startTop.x : '30%', y: changePos ? pos.startTop.y :47},
{x: '70%', y: 84}
],
[
{x: changePos ? pos.startBootom.x :'30%', y: changePos ? pos.startBootom.y :193},
{x: '70%', y: 157}
]
]
}
}
],
media: [
{
query: { minAspectRatio: 1 },
option: {
series: [{ center: ['70%', '50%'] }, { center: ['30%', '50%'] }]
}
}
]
}
}, [chartView])
const onChartReady = ($event) => {
console.log($event._chartsViews)
if (!chartView) {
setChartView($event._chartsViews)
}
}
return (
<div className="App">
<ReactEcharts
ref={myCharts}
style={{height: 240, width: '100%'}}
opts={{renderer: 'svg'}}
option={chartOptions}
onChartReady={(event)=>{onChartReady(event)}}
// 需要加onEvents,不然onChartReady不生效
onEvents={{resize: ()=>{console.log('resize')}}}
/>
</div>
);
}