apache / echarts

Apache ECharts is a powerful, interactive charting and data visualization library for browser
https://echarts.apache.org
Apache License 2.0
60.48k stars 19.62k forks source link

[Bug] Grid refresh with wrong display #18548

Open wdmhouston opened 1 year ago

wdmhouston commented 1 year ago

Version

5.4.2

Link to Minimal Reproduction

no, example page provided

Steps to Reproduce

  1. Create chart with the following script (modified from example),
<div id="main"></div>
<script>
        //Const data for grid refresh calculation
        const gs_min = 0.0;
        const gs_max = 4000;
        const gs = [
            { name: 'range 0-500', end: 500.0, start: 0.0, color: '#F5EC09' },
            { name: 'range 500-1000', end: 1000.0, start: 500.0, color: '#4CC6E0' },
            { name: 'range 100-1500', end: 1500.0, start: 1000.0, color: '#9DC2A6' },
            { name: 'range 1500-2000', end: 2000.0, start: 1500.0, color: '#E44253' },
            { name: 'range 2000-2500', end: 2500.0, start: 2000.0, color: '#F5EC09' },
            { name: 'range 2500-3000', end: 3000.0, start: 2500.0, color: '#4CC6E0' },
            { name: 'range 3000-3500', end: 3500.0, start: 3000.0, color: '#9DC2A6' },
            { name: 'range 3500-4000', end: 4000.0, start: 3500.0, color: '#E44253' },  
        ];

        //Dynamic series for grid
        var series_gs = [];
        //Refresh sereis_gs array from selected range percetage
        function create_series_gs(gs_start_percent, gs_end_percent) {
            console.log("create_series_gs called");
            series_gs = [];
            if (gs_start_percent < 0 || gs_end_percent > 100) {
                return; //continue
            }
            gs_start_value = gs_start_percent / 100.0 * (gs_max - gs_min) + gs_min;
            gs_end_value = gs_end_percent / 100.0 * (gs_max - gs_min) + gs_min;
            console.log(gs_start_value, gs_end_value);

            $.each(gs, function (index, v) {
                if (v.end < gs_start_value || v.start > gs_end_value) {
                    return;
                }
                var current_start = v.start > gs_start_value ? v.start:gs_start_value;
                var current_end = v.end < gs_end_value ? v.end : gs_end_value;
                series_gs.push(
                    {
                        data: [{
                            name: v.name,
                            value: 1
                        }],
                        tooltip: {
                            show: false
                        },
                        label: {
                            show: true,
                            position: 'inside',
                            formatter: '{b}',
                            offset: [0, 0],
                            textStyle: {
                                color: '#000'
                            },
                            rotate: 0
                        },
                        type: 'bar',
                        barGap: 0,
                        barWidth: (current_end - current_start) / (gs_end_value - gs_start_value) * 100.0 + '%',
                        itemStyle: {
                            normal: {
                                color: v.color,
                                borderColor: '#000',
                                borderWidth: 0,
                            }
                        },
                        xAxisIndex: 1,
                        yAxisIndex: 1
                    });
            });
            console.log("series_gs:");
            console.log(series_gs);
        }

        //User data for demo
        var data1 = [];
        var data2 = [];
        var data3 = [];

        var names = [
            'line1',
            'line2',
            'line3',
        ];

        var random = function (max) {
            return (Math.random() * max).toFixed(3);
        }

        for (var i = 0; i <= 40; i++) {
            data1.push([i * 100, 1 + random(5), random(2)]);
            data2.push([i * 100, 2 + random(10), random(2)]);
            data3.push([i * 100, 3 + random(10), random(2)]);
        }

        var series_data = [
            {
                name: names[0],
                type: 'line',
                label: {
                    emphasis: {
                        show: true
                    }
                },
                symbol: 'diamond',
                symbolSize: function (val) {
                    return val[2] * 10;
                },
                data: data1
            },
            {
                name: names[1],
                type: 'line',
                emphasis: {
                    scale: 3,
                    label: {
                        show: true,
                        position: 'top',
                        formatter: function (params) {
                            return params.value;
                        }
                    }
                },
                symbolSize: function (val) {
                    return val[2] * 10;
                },
                itemStyle: {
                    color: function (params) {
                        return 'rgba(30, 70, 50, ' + (params.value ? params.value[2] : 0) + ')';
                    }
                },
                data: data2
            },
            {
                name: names[2],
                type: 'line',
                label: {
                    show: true
                },
                symbolSize: function (val) {
                    return val[2] * 10;
                },
                data: data3
            }];

        //Create option from sereis_gs and sereis_data
        function create_option() {
            return {
                title: {
                    text: 'Grid Refresh Issue',
                    left: 'center',
                    top: 0
                },
                animation: false,
                aria: {
                    show: true
                },
                legend: {
                    data: names.slice(),
                    top: 10,
                    right: 30,
                    width: 0,
                    backgroundColor: 'rgba(0,50,50,0.2)',
                    orient: 'vertical',
                },
                toolbox: {
                    left: 'left',
                    feature: {
                        dataView: {},
                        saveAsImage: {},
                        dataZoom: {}
                    },
                    orient: 'vertical',
                },
                tooltip: {
                    trigger: 'item',
                    position: 'top',
                    borderWidth: 4
                },
                xAxis: [
                    {
                        type: 'value',
                        splitLine: {
                            show: false
                        },
                        min: 0.0,
                        max: 4000,
                        splitNumber: 23,
                        inverse: false,
                        zlevel: 5,
                    },
                    {
                        type: 'category',
                        gridIndex: 1,
                        axisLine: {
                            show: false,
                        },
                        axisLabel: {
                            show: false,
                        },
                        zlevel: 4,
                    },
                ],
                yAxis: [
                    {
                        type: 'value',
                        gridIndex: 0,
                        splitLine: {
                            show: true
                        },
                    },
                    {
                        type: 'value',
                        gridIndex: 1,
                        axisLabel: {
                            show: false
                        },
                        axisLine: {
                            show: false
                        },
                        splitLine: {
                            show: false
                        },
                        axisTick: {
                            show: false
                        }
                    }
                ],
                grid: [
                    {
                        top: 75,
                        bottom: 110
                    },
                    {
                        height: 30,
                        bottom: 50
                    },
                ],
                series: [...series_gs, ...series_data],
                dataZoom: [{
                    type: 'inside'
                }, {
                    type: 'slider',
                    height: 40,
                    bottom: 5
                }],
            };
        }

        var gschart;
        require([
            'echarts'
        ], function (echarts) {
            var chart = echarts.init(document.getElementById('main'));
            gschart = chart;
            create_series_gs(0, 100);
            chart.setOption(create_option());

            //datazoom event, handle chart option reset
            chart.on('datazoom', function (evt) {
                console.log('current evt:');
                console.log(evt);

                //recalculate sereis data for grid
                create_series_gs(evt.start, evt.end);

                chart.setOption(create_option());
                console.log('current option:');
                console.log(chart.getOption());
                console.log('current series_gs');
                console.log(series_gs);
            });
        });
    </script>
  1. Run script and it renders grid layout correctly. image

Current Behavior

When Zoom in, series data form grid are recalculated, but the grid part is rendered in a wrong way(some unexpected grid series show up). image

Given grid series have all correct data with correct barWidth: image

Expected Behavior

It is expected to render "grid series with correct barwidth" when zoom-in. image

Environment

- OS: Windows 11
- Browser: Chrome
- Framework: Javascript

Any additional comments?

No response

wdmhouston commented 1 year ago

I also tried to add "chart.clear();" before "chart.setOption(create_option());", but it didn't work.

helgasoft commented 1 year ago

Seems like an attempt to build a custom X-axis... Problem is in setOption: out-of-scope bars are not deleted, hence the remnant stripes at the end.

Here is a simple X-axis replacement - Demo Code