配置项如下
/*
* echart 不支持配置块之间的间隔,所以需要额外插入数据来生成间隔。
* 外环的间隔要比内环大一些,想要达到这样的效果,需要每个块的数据减去一个固定的数值
*/
/*
* 确认问题:
* 1. hover选中时,内环颜色、外环颜色、字体颜色如何设置
* 2. 外环的背景色是否都一样
* 3. label 固定显示在外环的中间。如果要调整 label 离块的距离,需要调整外环的大小
*/
const sourceData = [
{
name: '小学',
value: 129
},
{
name: '中学',
value: 224
},
{
name: '高中',
value: 384
},
{
name: '大专',
value: 420
},
{
name: '本科',
value: 558
},
{
name: '研究生',
value: 238
}
]
const total = sourceData.reduce((num, item) => {
num += item.value
return num
}, 0)
// 内环间隔距离
const inSplitWidth = 3
// 为了实现内环间隔距离,需要额外插入的数值。200 只是个系数,值越大,单位间隔的距离越小。
const inSplitValue = Math.floor(total / (200 / inSplitWidth))
// 外环间隔比内环间隔大的值
const itemSplitWidth = 2
// 外环间隔距离
const outSplitWidth = inSplitWidth + itemSplitWidth
// 为了实现外环间隔距离,需要额外插入的数值。
const outSplitValue = Math.floor(total / (200 / outSplitWidth))
// 内环数据的总数
const valueTotal = total + inSplitValue * sourceData.length
function getTextAngle(currentAngle, angle) {
currentAngle = currentAngle + angle
console.log('currentAngle', currentAngle)
if (currentAngle <= 90) {
return -currentAngle;
} else if (currentAngle <= 180 && currentAngle > 90) {
return 180 - currentAngle;
} else if (currentAngle < 270 && currentAngle > 180) {
return 180 - currentAngle;
} else if (currentAngle < 360 && currentAngle >= 270) {
return 360 - currentAngle;
}
}
// 内环数据。在原数据的后面添加间隔数据(间隔块设置颜色透明)
const valueData = sourceData.reduce((arr, item) => {
const currentTotal = arr.reduce((total, item) => {
total += item.value
return total
}, 0)
const currentAngle = 360 * (currentTotal / valueTotal)
const angle = 360 * (item.value / valueTotal) / 2
console.log('current angle', currentAngle)
arr.push({
name: item.name,
value: item.value,
itemStyle: {
},
label: {
lineHeight: 80,
rotate: getTextAngle(currentAngle, angle)
}
}, {
name: '',
value: inSplitValue,
itemStyle: {
color: 'transparent',
opacity: 0
},
label: {
show: false
},
labelLine: {
show: false
}
})
return arr
}, [])
// 原数据需要减去的值(外环每块的数据都要比原数据少一点才能达到外环嵌在内环的效果)
const itemReduced = outSplitValue - inSplitValue
// 外环数据
const outValueData = sourceData.reduce((arr, item, index) => {
const currentTotal = arr.reduce((total, item) => {
total += item.value
return total
}, 0)
const currentAngle = 360 * (currentTotal / valueTotal)
const angle = 360 * (item.value / valueTotal) / 2
arr.push({
name: item.name,
value: item.value - itemReduced,
itemStyle: {
// color: 'rgba(160, 60, 60, 0.3)'
},
label: {
color: 'rgb(160, 60, 60)',
position: 'inside',
align: 'center',
// lineHeight: 20,
// verticalAlign: 'top',
rotate: getTextAngle(currentAngle, angle)
}
}, {
name: '',
value: outSplitValue,
itemStyle: {
color: 'transparent',
opacity: 0
},
label: {
show: false
},
labelLine: {
show: false
}
})
return arr
}, [])
option = {
title: {
text: 'Awesome Chart'
},
series: [
{
type: 'pie',
data: valueData,
// startAngle: 0,
label: {
show: false,
position: 'outside',
align: 'center',
verticalAlign: 'middle',
// rotate: true,
formatter(params) {
// console.log('label params', params)
return params.data.name
}
},
labelLine: {
show: false
},
emphasis: {
scale: true,
scaleSize: 20
},
blur: {
itemStyle: {
opacity: 1
}
},
// labelLayout(params) {
// return {
// x: params.rect.x,
// y: params.rect.y + params.rect.height / 2
// }
// },
radius: ['60%', '80%'],
itemStyle: {
// borderWidth: 5,
// borderColor: '#000',
// borderRadius: [10, 20],
// opacity: 0.5,
// borderCap: 'square',
// borderJoin: 'round'
}
},
{
type: 'pie',
startAngle: -360 * (itemReduced / 2 / valueTotal) + 90,
radius: ['79%', '95%'],
itemStyle: {
// color: 'rgba(160, 60, 60, 0.5)',
opacity: 0.3,
// borderWidth: 15,
// borderColor: 'rgba(0, 0, 0, 0)'
},
label: {
show: true,
formatter(params) {
return '{a|'+ params.data.name +'}'
},
rich: {
a: {
lineHeight: 50,
color: 'red'
}
}
},
labelLine: {
show: false,
length: 10,
},
data: outValueData
}
]
};