EmilioGarzaC
EmilioGarzaC

Reputation: 84

How to center legend items along x axis in Apache Echarts?

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.

enter image description here

I'm using the default code for the Stacked Horizontal Bar, which you can find here.

Upvotes: 2

Views: 101

Answers (1)

kikon
kikon

Reputation: 8890

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; max-width:400px'></div>
<script src="https://fastly.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>

Upvotes: 2

Related Questions