1

I have a Bar Chart made with Apache E Charts. For the label values, there is a white outline bordering the text. I have tried adjusting several options to try and get rid of it, but have had no luck. What is the option that would allow me to get rid of this white outline and just show black text?

Bar Chart

Full options code per request:

const option = useMemo(() => {
if (data) {
  return {
    dataset: {
      source: data,
    },
    textStyle: {
      fontFamily: "'Open Sans', sans-serif",
    },
    title: {
      text: "AR Aging",
      left: "left",
      textStyle: {
        fontFamily: "'Open Sans', sans-serif",
      },
    },
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "shadow",
      },
    },
    color: ["#ff4242", "#ff4242", "#FFA500", "#fddc20", "#209c05"],
    grid: { containLabel: true, height: "70%" },
    xAxis: {
      name: "Amount",
      axisLabel: {
        formatter: (params) => formatMoney(params),
      },
    },
    yAxis: { name: "Period", type: "category" },
    series: [
      {
        type: "bar",
        colorBy: "data",
        legendHoverLink: true,
        label: {
          show: true,
          formatter: (params) => formatMoney(params.value[0]),
        },
        labelLayout(params) {
          return {
            x: params.rect.x + 10,
            y: params.rect.y + params.rect.height / 2,
            verticalAlign: "middle",
            align: "left",
          };
        },
        encode: {
          x: "Amount",
          y: "Period",
        },
      },
    ],
  };
}
}, [data]);

formatMoney() takes a number and returns a formatted currency string. And my data object is something like this:

const data = [
      ["Amount", "Period"],
      [100000, "180+"],
      [90000, "91-180"],
      [80000, "61-90"],
      [70000, "31-60"],
      [60000, "0-30"],
    ];
Locke
  • 131
  • 1
  • 10

1 Answers1

1

Edit

What you found is very interesting, I think it's a bug or at least a missing feature in echarts - setting, in a bar chart, the labelLayout x or y properties messes with the coloring of the label. The cause for that is the fact that echarts (in zrender's function Element#updateInnerText) doesn't know that the label isInside. So, if you set x or y, it appears that it will use coloring for labels that are placed outside of the bar, on the background of the chart itself.

Let's look at a couple of possible solutions:

  1. avoid setting x and y in labelLayout, but use dx and dy instead. The formula used in the original code to set the label 10px from the left margin of the bar was:
    x: params.rect.x + 10;
    the equivalent dx setting (taking into account that the default position is at the center of the params.rect is:
    dx: -params.rect.width/2 + 10.

    Note the animation of the labels in this case. That can of course be disabled by disabling the animation of the series (series[].animation: false).

  2. set an explicit backgroundColor: transparent in the label option. Note that transparent is the default value for label, but the result is not exactly the default behavior of inside label coloring - text color is black or white depending on the color of the bar.

    Note that for this second solution, one may as well set explicitly the color of the label, which precludes the need to set the backgroundColor. So one may juts use color: black instead of backgroundColor: transparent.

In the snippet below the same bars are shown thrice: for the topmost one, the first solution (use dx instead of x) and the default coloring, for the second one the backgroundColor: transparent solution, and for the third one, the original version with the undesired outline.

const data = [
  ["Amount", "Period"],
  [100000, "180+"],
  [80000, "61-90"],
  [70000, "31-60"],
  [60000, "0-30"]
];

const option = {
  dataset: {
    source: data
  },
  color: ["#ff4242", "#FFA500", "#fddc20", "#209c05"],
  xAxis: {
    name: "Amount",
  },
  yAxis: {
    name: "Period",
    type: "category"
  },
  series: [{ // solution 1: replace x by dx
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
        backgroundColor: 'transparent'
      },
      labelLayout(params) {
        return {
          dx: -params.rect.width / 2 + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    },
    { // solution 2: backgroundColor: 'transparent'
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
        backgroundColor: 'transparent'
        // or:
        // color: 'black'
      },
      labelLayout(params) {
        return {
          x: params.rect.x + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    },
    { // original erroneous outline
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
      },
      labelLayout(params) {
        return {
          x: params.rect.x + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    }
  ]
};

const myChart = echarts.init(document.getElementById('chart-container'), null, {
  renderer: 'svg'
});
myChart.setOption(option);
<div class="chart" id="chart-container" style="height:400px; width: 80vw"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js"></script>

The next snippet does the same, with the only difference that the chart background color is black:

const data = [
  ["Amount", "Period"],
  [100000, "180+"],
  [80000, "61-90"],
  [70000, "31-60"],
  [60000, "0-30"]
];

const option = {
  backgroundColor: '#000',
  dataset: {
    source: data
  },
  color: ["#ff4242", "#FFA500", "#fddc20", "#209c05"],
  xAxis: {
    name: "Amount",
  },
  yAxis: {
    name: "Period",
    type: "category"
  },
  series: [{ // solution 1: replace x by dx
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
        backgroundColor: 'transparent'
      },
      labelLayout(params) {
        return {
          dx: -params.rect.width / 2 + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    },
    { // solution 2: backgroundColor: 'transparent'
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
        backgroundColor: 'transparent'
        // or:
        // color: 'black'
      },
      labelLayout(params) {
        return {
          x: params.rect.x + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    },
    { // original erroneous outline
      type: "bar",
      colorBy: "data",
      label: {
        show: true,
      },
      labelLayout(params) {
        return {
          x: params.rect.x + 10,
          verticalAlign: "middle",
          align: "left"
        };
      },
      encode: {
        x: "Amount",
        y: "Period"
      }
    }
  ]
};

const myChart = echarts.init(document.getElementById('chart-container'), null, {
  renderer: 'canvas'
});
myChart.setOption(option);
<div class="chart" id="chart-container" style="height:400px; width: 80vw"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js"></script>

One can see that the coloring of the labels in the first series (topmost bar in each group) makes a best contrast to the color of the bar, the second series has a fixed color, and the third (the original one) the color is to ensure the best contrast to the background color


Original post

I think you somehow activated the shadow of the label text. That's not the default behavior, see this echarts example.

So to get something like you have I had to set the label option as

    label: {
        textShadowColor: 'white',
        textShadowOffsetX: 1,
        textShadowOffsetY: 1,
        textShadowBlur: 2,
        color: 'black',
        show: true
      }

example fork - first series

So to cancel it, I expect you may set any of

   label: {
        textShadowColor: 'transparent', // or
        textShadowOffsetX: 0,  textShadowOffsetY: 0, // or
        textShadowBlur: 0,
   }

Please let me know if that doesn't help.

kikon
  • 3,670
  • 3
  • 5
  • 20
  • Thanks @kikon. I am not adjusting the shadow text to my knowledge. I've updated the post with the full options object being loaded. – Locke Jun 13 '23 at 19:51
  • With the code, I was able to reproduce the problem in react with `echarts-for-react`; it's not the text shadow; I'll look into it to see how can you disable it. – kikon Jun 14 '23 at 16:58
  • It appears to be the result of the use of `labelLayout`, although there is no reasonable connection. A simple fix is to disable `x` and `y` in `labelLayout` result and set `dx: -params.rect.width / 2 + 10`, see this [codesandbox](https://codesandbox.io/s/elastic-taussig-9vhc5t?file=/src/App.js). I'll look later today to understand what's happening and how can one set `x` and `y` without getting the white outline, and I'll update my answer. – kikon Jun 14 '23 at 17:31
  • Thank you @kikon! I will likely go with the first option. Should I report this to Echarts as a bug?? – Locke Jun 15 '23 at 16:35
  • It's not really a bug, if you consider for instance the case when a label is half inside and half outside. I'll think in the following days about filling a feature request - there should be a way to request inside type colors. – kikon Jun 15 '23 at 16:45
  • But I have to look if there isn't one already :) I thought that could be achieved by setting `label.position: 'inside'` [doc ref](https://echarts.apache.org/en/option.html#series-bar.label.position), but it's superseded by the `layout`. I'll let you know here if I find out something new. – kikon Jun 15 '23 at 16:50