echartsapache-echarts

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.


Solution

  • 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>