chartjs / Chart.js

Simple HTML5 Charts using the <canvas> tag
https://www.chartjs.org/
MIT License
64.9k stars 11.93k forks source link

Skip only last border in stacked bar #10360

Open liondog opened 2 years ago

liondog commented 2 years ago

Expected behavior

Separating stacked bar elements with a border / separator improves readability and is a common UI pattern: image

For example see https://www.ibm.com/design/language/data-visualization/charts or https://developers.google.com/chart/interactive/docs/gallery/barchart#stacked-bar-charts

Current behavior

Chart.js provides

borderWidth: { top: 1 }

that shows the separators as expected. But the issue is that the separator on the last stacked bar element is not hidden. This leads to the unwanted side-effect that the stacked bar is not pixel perfect anymore to the respective value axis:

image

The issue is that skipping only the last border is not possible using

elements: { bar: { borderSkipped: 'end' // 'top' shows the same unexpected behavior } }

Reproducible sample

https://codepen.io/mimizan/pen/ZErKoWp

Optional extra steps/info to reproduce

No response

Possible solution

Simply not providing a top border on the last element programmatically is not a solution as the user might want to show/hide individual bar elements via the legend. Determining the last visible stacked bar element via ScriptableOptions looks impossible or very complex.

https://github.com/chartjs/Chart.js/pull/8941 changed the global bar border radius option to only apply to the last bar element.

I'd propose that:

Context

No response

chart.js version

v3.7.1

Browser name and version

Chrome Canary, Firefox Developer Edition

Link to your project

No response

LeeLenaleee commented 2 years ago

You can use a scriptable function for this:

options: {
  elements: {
    bar: {
      borderSkipped: (ctx) => {
        const {
          chart,
          datasetIndex
        } = ctx;

        const topVisebleBar = chart.data.datasets.reduce((acc, curr, i) => {
          if (!chart.getDatasetMeta(i).hidden) {
            acc = i;
          }

          return acc;
        }, 0);

        return datasetIndex === topVisebleBar ? 'end' : 'start'
      }
    }
  }
}

https://codepen.io/leelenaleee/pen/RwQVJZW

kurkle commented 2 years ago

In the original pen there are options defined twice, that is not something you can do.

Is this what you are trying to do: https://codepen.io/kurkle/pen/MWQvMeG ?

(Probably the scriptable option that @LeeLenaleee suggested is what you need)