Search code examples
javascripttypescriptchartsvisualizationecharts

How to Dynamically Set MarkArea Labels Background Color Based on X-axis Range in Apache ECharts?


I am currently working on a project involving Apache ECharts, and I'm trying to achieve a dynamic visual effect with markArea labels. Specifically, I want to dynamically set the background color of these labels based on user-defined x-axis value ranges. Here's the code snippet I'm using:

const series = [
    {
      name: 'Electricity',
      type: 'line',
      smooth: true,
      data: [[0, 100], [100, 300], [300, 100], [400, 300]],
      markLine: {
        data: [{ xAxis: 200 }]
      },
      markArea: {
        itemStyle: {
          color: 'transparent'
        },
        data: [
          [
            {
              name: 'Run',
              xAxis: 'min'
            },
            {
              xAxis: '200'
            }
          ],
          [
            {
              name: 'Equilibration',
              xAxis: '200'
            },
            {
              xAxis: 'max'
            }
          ]
        ]
      }
    }
  ]

In this code, I have defined two markArea sections: "Run" and "Equilibration." For the "Run" section, I want to set the background color from the x-axis minimum value to 200, and for the "Equilibration" section, I want to set the background color from 200 to the x-axis maximum value.

I have attempted to solve this issue by adjusting the data array within the markArea configuration. However, the background color is not behaving as expected, and the labels' widths are not spanning the specified x-axis value ranges.

Could someone please provide guidance on how to achieve this effect with Apache ECharts? I am seeking a solution that allows me to set the background color for each label based on the specified x-axis range. Your insights and solutions would be highly appreciated. Thank you in advance for your help!

I've provided an example link that showcases the issue I am facing ECharts Example

Desired output: enter image description here


Solution

  • You can adjust the label and background of each markArea within the edge points specifying the are itself:

    markArea: {
        data: {
            [
                [
                    {    // top left point of the area
                        ...,
                        itemStyle: {...},    // background
                        label: {...}         // label
                    },
                    {    // bottom right point of the area
                        ...
                    }
                ],
                ...
            ]
        }
    }
    

    On which of the two edge points you set the options doesnt matter but it seems like the first one overwrites the second one.

    Adjusting the label to fit your image is a little more complicated. The easiest way I can think of is increasing the maximum of the yAxis a little to make room for the label box and putting it inside the grid + instead of using the label property we just use another markArea. Note, that you need to compute the lower bound of the area somehow. The other option would be to use the label which would force you to calculate the width of the box somehow.

    Here is an example of the first variant:

    const data = [[0, 100], [100, 300], [300, 100], [400, 300]];
    const ymax = Math.max(...data.map(item => item[1]));
    option = {
      title: {
        text: 'Distribution of Electricity',
        subtext: 'Fake Data'
      },
      xAxis: {
        type: 'value',
      },
      yAxis: {
        type: 'value',
        max: value => value.max + (value.max - value.min) * 0.06,
        axisLabel: {showMaxLabel: false}
      },
      series: [
        {
          name: 'Electricity',
          type: 'line',
          smooth: true,
          data: data,
          markLine: {
            symbol: 'none',
            lineStyle: {
              type: 'solid',
              width: 2,
              color: 'grey'
            },
            label: {show: false},
            data: [{ xAxis: 200 }]
          },
          markArea: {
            silent: true,
            data: [
              [
                {
                  name: 'Run',
                  xAxis: 'min',
                  itemStyle: {    // <--- adjust background of markedArea here
                    color: 'lightgreen',
                    opacity: 0.5
                  },
                  label: {        // <--- adjust label of markedArea here
                    position: 'insideTop',
                    fontSize: 20,
                    fontWeight: 'bold',
                    color: 'white',
                    distance: 12
                  }
                },
                {xAxis: 200,},
              ],
              [     // helper area to make background of the label
                {
                  xAxis: 'min',
                  yAxis: ymax,
                  itemStyle: {color: 'grey'}
                },
                {xAxis: 200}
              ],
              [
                {
                  name: 'Equilibration',
                  xAxis: '200',
                  itemStyle: {
                    color: 'orange',
                    opacity: 0.5
                  },
                  label: {
                    position: 'insideTop',
                    fontSize: 20,
                    fontWeight: 'bold',
                    distance: 12
                  }
                },
                {xAxis: 'max'}
              ],
              [
                {
                  xAxis: 200,
                  yAxis: ymax,
                  itemStyle: {color: 'lightgrey'}
                },
                {xAxis: 'max'}
              ],
            ]
          }
        }
      ]
    };
    

    If you want to use the second approach, here is an example with fixed values for the width. You could use convertToPixel function to calculate the width from values in your graph.

    option = {
      title: {
        text: 'Distribution of Electricity',
        subtext: 'Fake Data'
      },
      xAxis: {
        type: 'value',
      },
      yAxis: {
        type: 'value',
      },
      series: [
        {
          name: 'Electricity',
          type: 'line',
          smooth: true,
          data: [[0, 100], [100, 300], [300, 100], [400, 300]],
          markLine: {
            symbol: 'none',
            lineStyle: {
              type: 'solid',
              width: 2,
              color: 'grey'
            },
            label: {show: false},
            data: [{ xAxis: 200 }]
          },
          markArea: {
            data: [
              [
                {
                  name: 'Run',
                  xAxis: 'min',
                  itemStyle: {    // <--- adjust background of markedArea here
                    color: 'lightgreen',
                    opacity: 0.5
                  },
                  label: {        // <--- adjust label of markedArea here
                    backgroundColor: 'grey',
                    borderColor: 'grey',
                    width: 570,
                    height: 24,
                    distance: 6,
                    borderWidth: 6,
                    fontSize: 20,
                    fontWeight: 'bold',
                    color: 'white'
                  }
                },
                {
                  xAxis: '200',
                },
              ],
              [
                {
                  name: 'Equilibration',
                  xAxis: '200',
                  itemStyle: {
                    color: 'orange',
                    opacity: 0.5
                  },
                  label: {
                    backgroundColor: 'lightgrey',
                    borderColor: 'lightgrey',
                    width: 570,
                    height: 24,
                    distance: 6,
                    borderWidth: 6,
                    fontSize: 20,
                    fontWeight: 'bold',
                  }
                },
                {
                  xAxis: 'max'
                }
              ]
            ]
          }
        }
      ]
    };