Search code examples
rdata-visualizationecharts4r

Is it possible to change borderColor for area bands (e_band2) in echarts4r?


Goal:
In my plot, I would like to have different colors for the border and the area of an area band. The plot was created with echarts4r using the e_band2() function.

Problem:
In the documentation I read that area bands can be customized through the itemStyle argument of the function. This works fine for all the other options (borderWidth, borderType, shadowBlur, shadowColor) I tested, but not for borderColor. However, the borderColor option seems to work at least partially, because the symbol in the legend has the desired border color, but not the area band in the plot.

Does anyone know if there is another way to change the border color or is this a bug?

enter image description here

Reprex:

library(echarts4r)
library(dplyr)

data(EuStockMarkets)

as.data.frame(EuStockMarkets) |>
  dplyr::slice_head(n = 200) |>
  dplyr::mutate(day = 1:dplyr::n()) |>
  e_charts(day) |>
  e_band2(DAX, SMI, itemStyle = list(
    borderWidth = 1,
    color = "green",
    borderColor = "red"
  )) |>
  e_y_axis(scale = TRUE)

Solution

  • For some reason, it's hardcoded to ignore your style settings for the stroke color (in SVG, that's the lines) by echarts4r.

    However, you can change it back.

    I've taken the code from the echarts4r package for the JS function renderBand and modified it to use the settings that are defined in the plot call (which is what you wanted and expected).

    Note that I've made this a string.

    renderBands2 <- "
      function renderBands2(params, api) {
        if (params.context.rendered) return;
        params.context.rendered = true;
        
        /* set polygon vertices */
        let points = [];
        let i = 0;
        while (typeof api.value(0,i) != 'undefined' && !isNaN(api.value(0,i))) {
          points.push(api.coord([api.value(0,i), api.value(1,i)]));  /* lo */
          i++;
        }
        for (var k = i-1; k > -1 ; k--) {
          points.push(api.coord([api.value(0,k), api.value(2,k)]));  /* up */
        }
        return {
          type: 'polygon',
          shape: {
            points: echarts.graphic.clipPointsByRect(points, {
              x: params.coordSys.x, y: params.coordSys.y,
              width: params.coordSys.width, height: params.coordSys.height
            })}, style: api.style()
        };
      }"
    

    Apply this to your plot with htmlwidgets::onRender. This calls for the plot to be re-rendered with this new function for the data series.

    as.data.frame(EuStockMarkets) |>
      dplyr::slice_head(n = 200) |>
      dplyr::mutate(day = 1:dplyr::n()) |>
      e_charts(day) |>
      e_band2(DAX, SMI, itemStyle = list(
        borderWidth = 1, color = "green", borderColor = "red")) |>
      e_y_axis(scale = TRUE) %>% 
      htmlwidgets::onRender(paste0(
        "function(el, data) {
        ", renderBands2,
        "
        chart = get_e_charts(el.id);
        opts = chart.getOption();
        opts.series[0].renderItem = renderBands2;
        chart.setOption(opts, true); /* all better! */
        }"))
    

    enter image description here