效果图
将下面代码复制到 echarts.apache.org/examples/zh… 可直接浏览
const data = [90, 40, 20] //数据
const total = data.reduce((a,b)=> a+b) //总数,用来计算占比和显示背景圆柱
//画圆柱的椭圆顶部
function pathEllipse(ctx, centerX, centerY, a, b) {
var k = 0.5522848,
ox = a * k, // 水平控制点偏移量
oy = b * k; // 垂直控制点偏移量
//从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
ctx.moveTo(centerX - a, centerY);
ctx.bezierCurveTo(
centerX - a,
centerY - oy,
centerX - ox,
centerY - b,
centerX,
centerY - b
);
ctx.bezierCurveTo(
centerX + ox,
centerY - b,
centerX + a,
centerY - oy,
centerX + a,
centerY
);
ctx.bezierCurveTo(
centerX + a,
centerY + oy,
centerX + ox,
centerY + b,
centerX,
centerY + b
);
ctx.bezierCurveTo(
centerX - ox,
centerY + b,
centerX - a,
centerY + oy,
centerX - a,
centerY
);
ctx.closePath();
ctx.stroke();
}
//画圆柱的侧面
function pathBottom(ctx, centerX, centerY, a, b, bottomYAxis) {
var k = 0.5522848,
ox = a * k, // 水平控制点偏移量
oy = b * k; // 垂直控制点偏移量
const [x, y] = [centerX, centerY];
ctx.moveTo(x + a, y); //椭圆右端点开始顺时针绘制两条曲线
ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
ctx.lineTo(x - a, bottomYAxis); //从上到下的直线
const [bx, by] = [centerX, bottomYAxis]; //底部椭圆左侧端点开始逆时针绘制两条曲线
ctx.bezierCurveTo(bx - a, by + oy, bx - ox, by + b, bx, by + b);
ctx.bezierCurveTo(bx + ox, by + b, bx + a, by + oy, bx + a, by);
ctx.lineTo(bx + a, centerY); //从下到上的直线
ctx.closePath();
ctx.stroke();
}
// 注册椭圆
echarts.graphic.registerShape('shapeEllipse', echarts.graphic.extendShape({
shape: {},
buildPath(ctx, shape) {
const { EllipseBaseData, EllipsePosition } = shape;
pathEllipse(
ctx,
EllipsePosition.centerX,
EllipsePosition.centerY,
EllipseBaseData.a,
EllipseBaseData.b
);
}
}));
// 注册圆柱侧面
echarts.graphic.registerShape('shapeCylinderSide', echarts.graphic.extendShape({
shape: {},
buildPath(ctx, shape) {
const { EllipseBaseData, EllipsePosition } = shape;
pathBottom(
ctx,
EllipsePosition.centerX,
EllipsePosition.centerY,
EllipseBaseData.a,
EllipseBaseData.b,
EllipsePosition.bottomYAxis
);
}
}));
option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed']
},
yAxis: {
type: 'value',
max: total
},
tooltip: {
show: true,
trigger: 'item',
backgroundColor: '#ffffff',
padding: [8, 16, 12, 16],
borderWidth: 0,
extraCssText:
'box-shadow: 0px 4px 10px rgba(123, 135, 178, 0.2);border-radius: 6px;',
formatter: (params) => {
return `<div>
<span style="width:8px;height:8px;display:inline-block;border-radius:50%;margin-right: 6px; vertical-align:baseline; background: ${
['#0071FA', '#2eb3fd', '#17ceda'][params.dataIndex] || '#0071FA'
};"></span>
<span>${params.name}学习知识点</span>
<span style="font-size:14px;line-height: 20px;
font-weight: 700;color:#2B2E35;padding-left: 16px;">${params.value}</span>
</div>`;
},
textStyle: {
color: '#656A72',
fontSize: 12,
lineHeight: 18
}
},
grid: {
width: 300,
height: 200
},
series: [
{
data: data,
type: 'custom',
itemStyle: {
color: (params) => {
return (
[
'rgba(0, 113, 250, 1)',
'rgba(46, 179, 253, 1)',
'rgba(22, 206, 218, 1)'
][params.dataIndex] || 'rgba(0, 113, 250, 1)'
);
}
},
renderItem: (params, api) => {
// 基础坐标
const basicsCoord = api.coord([api.value(0), api.value(1)]);
// 圆柱底部 y 轴
const bottomYAxis = api.coord([api.value(0), 0])[1];
// 背景圆柱顶部 y 轴
const bgTopYAxis = api.coord([api.value(0), total])[1];
const EllipseBaseData = {
//椭圆的数据
a: 19, //半长轴
b: 5 //半短轴
};
return {
type: 'group',
children: [
{
type: 'shapeEllipse', //背景顶部的椭圆
shape: {
EllipseBaseData,
EllipsePosition: {
centerX: basicsCoord[0],
centerY: bgTopYAxis
}
},
style: {
fill: api.style().fill.replace(', 1)', ', 0.4)')
}
},
{
type: 'shapeCylinderSide', //背景椭圆的侧面
shape: {
EllipseBaseData,
EllipsePosition: {
centerX: basicsCoord[0],
centerY: bgTopYAxis,
bottomYAxis
}
},
style: {
fill: api.style().fill.replace(', 1)', ', 0.2)')
}
},
{
type: 'shapeEllipse', //实际顶部的椭圆
shape: {
EllipseBaseData,
EllipsePosition: {
centerX: basicsCoord[0],
centerY: basicsCoord[1]
}
},
style: {
fill: api.style().fill
}
},
{
type: 'shapeCylinderSide', //实际椭圆的侧面
shape: {
EllipseBaseData,
EllipsePosition: {
centerX: basicsCoord[0],
centerY: basicsCoord[1],
bottomYAxis
}
},
style: {
fill: api.style().fill.replace(', 1)', ', 0.6)')
}
},
{
type: 'text', //圆柱顶部数据 xx%
style: {
text: Math.round(api.value(1)*100/total) + '%',
fontSize: 10,
x: basicsCoord[0],
y: bgTopYAxis - 15,
align: 'center',
verticalAlign: 'middle',
fill: 'rgba(171, 176, 191, 1)'
}
}
]
};
}
}
]
};