I want to align the different legend items in Echarts to the center, instead of having the default left alignment. Is this possible? I haven't been able to find a way to do this in the documentation.
I'm using the default code for the Stacked Horizontal Bar, which you can find here.
There isn't an option that can be set to center the legend items, the way
you want. The items are arranged by the function boxLayout
,
layout.ts#L57
that when an item doesn't fit on the current line, adds it to the next line
at x = 0
, see layout.ts#L87.
One can rearrange the legend items after the chart has rendered its
legend (and after each chart resize
) but that is a hacky solution that
accesses the undocumented inner structures of the chart that might
change without notice in future versions of the library:
myChart.setOption(option);
centerLegendItems(myChart);
window.addEventListener('resize', ()=>{
myChart.resize();
centerLegendItems(myChart)
});
function centerLegendItems(chart){
const legendView = Object.values(chart._componentsMap).find(view => view.type === 'legend.plain');
const maxWidth = legendView.group.getBoundingRect().width;
const legendItems = legendView.group._children.find(group => group._children.length > 0)._children;
const nItems = legendItems.length;
const positions = legendItems.map(group => ({x: group.x, y: group.y, width: group.getBoundingRect().width}));
const lines = [];
let line = [positions[0]];
lines.push(line);
for(let i = 1; i < nItems; i++){
const position = positions[i];
if(Math.abs(position.y - line[0].y) < 2){
line.push(position);
}
else{
line = [position];
lines.push(line);
}
}
const deltaX = [];
for(const line of lines){
const lastItem = line[line.length-1];
const lineWidth = lastItem.x + lastItem.width;
const deltaLine = (maxWidth - lineWidth) / 2;
deltaX.push(...Array(line.length).fill(deltaLine));
}
for(let i = 0; i < nItems; i++){
legendItems[i].x += deltaX[i];
}
}
In a snippet with the example chart:
const myChart = echarts.init(document.getElementById('main'));
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value'
},
yAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
series: [
{
name: 'Direct',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [320, 302, 301, 334, 390, 330, 320]
},
{
name: 'Mail Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: 'Affiliate Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: 'Video Ad',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [150, 212, 201, 154, 190, 330, 410]
},
{
name: 'Search Engine',
type: 'bar',
stack: 'total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [820, 832, 901, 934, 1290, 1330, 1320]
}
]
};
myChart.setOption(option);
centerLegendItems(myChart);
window.addEventListener('resize', ()=>{myChart.resize(); centerLegendItems(myChart)});
function centerLegendItems(chart){
const legendView = Object.values(chart._componentsMap).find(view => view.type === 'legend.plain');
const maxWidth = legendView.group.getBoundingRect().width;
const legendItems = legendView.group._children.find(group => group._children.length > 0)._children;
const nItems = legendItems.length;
const positions = legendItems.map(group => ({x: group.x, y: group.y, width: group.getBoundingRect().width}));
const lines = [];
let line = [positions[0]];
lines.push(line);
for(let i = 1; i < nItems; i++){
const position = positions[i];
if(Math.abs(position.y - line[0].y) < 2){
line.push(position);
}
else{
line = [position];
lines.push(line);
}
}
const deltaX = [];
for(const line of lines){
const lastItem = line[line.length-1];
const lineWidth = lastItem.x + lastItem.width;
const deltaLine = (maxWidth - lineWidth) / 2;
deltaX.push(...Array(line.length).fill(deltaLine));
}
for(let i = 0; i < nItems; i++){
legendItems[i].x += deltaX[i];
}
}
<div id='main' style='height: 300px'></div>
<script src="https://fastly.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>