chartjs / chartjs-plugin-datalabels

Chart.js plugin to display labels on data elements
https://chartjs-plugin-datalabels.netlify.app
MIT License
870 stars 471 forks source link

Position reset on bar hover #253

Closed Creatium closed 3 years ago

Creatium commented 3 years ago

ChartJS 3, Datalabels 2.0 RC.

I explained my feature request, based on what I'm trying to achieve for my graphs here: #252

So what I am trying to do is to basically adjust anchor and align settings with functions, based on the height if bar. But I see another issue, a probable bug, that pops out.

Lets take anchor as example. See Codepen: https://codepen.io/meexo/pen/rNypRMP

If you hover over any bar, you will see that labels are reset to center, though my setting says end.

Here is part of my anchor code:

anchor: context => {
    let bar = context.chart.getDatasetMeta(context.datasetIndex).data[context.dataIndex];
    let barHeight = Math.abs(bar.y-bar.base);
    const threshold = 20;

    if (barHeight < threshold) {
        return 'end'
    }
    return 'center'
}
simonbrunel commented 3 years ago

Scriptable options are evaluated only one time before animations but not during, neither at the end. In your case, you are reading the bar y and base values when bars are at their minimal height (beginning of the animation), thus the 'end' anchor. When hovering bars (after the animations are completed), scriptable options are re-evaluated: y and base are at their final values, thus the center anchor.

Instead, you should read the final values in your scriptable option:

anchor: ({ chart, datasetIndex, dataIndex }) => {
  const bar = chart.getDatasetMeta(datasetIndex).data[dataIndex];
  const { base, y } = bar.getProps(['base', 'y'], true); // true for "final" values
  const barHeight = Math.abs(y - base);
  const threshold = 20;

  return barHeight < threshold ? 'end' : 'center'
}

https://codepen.io/simonbrunel/pen/MWprRYg

Creatium commented 3 years ago

Thanks for the reply and solution. But look at this "final" solution and it still seems to be wonky. Am I missing something again?

Based on your code simply added dynamic offset parameter. Try hovering over bar or pressing "update": https://codepen.io/meexo/pen/JjWMqMe

If my use case, after setting new data for the chart and updating it, bar.getProps(['base', 'y']) returns 0 0.

simonbrunel commented 3 years ago

I think the issue is that scriptable options are evaluation after each dataset update, meaning that the bar geometry for dataset i+1 is not yet available. That's why getProps(['base', 'y']) for bar at datasetIndex: 1 returns 0 when evaluating options for bar at datasetIndex: 0. There might be a way to delay scriptable options until all bar are computed, though I don't think you should use this technique to implement the design suggested in #252.

Instead, what about this solution?

Creatium commented 3 years ago

Yes its a pretty good solution, as long as user does not turn off C dataset. But thanks for the explanation. Documentation on this matter is very hard to understand for me, so this is why it was unclear to me.

Though this solution does not seem to work with an updated data for chart (i.e. when new datasets are provided).