chartjs / chartjs-plugin-annotation

Annotation plugin for Chart.js
MIT License
609 stars 329 forks source link

Configurable clip for each annotation #874

Open zk118 opened 1 year ago

zk118 commented 1 year ago

Feature Proposal

Would it be possible to have the clip property per annotation so we can control it independently ? What would be a workaround for this ? Thanks in advance.

Possible Implementation

No response

stockiNail commented 1 year ago

@zk118 the only way currently to unclip the chart area is to set clip: false at plugin level, for all annotations. Unfortunately no workaround.

This topic was already raised with @kurkle (I haven't found in which issue or PR) and there were some doubts if to implement it or not. Anyway, the implementation could be a breaking change (therefore it needs a major version) because adding the clip option at annotation level, should be remove to the plugin main config and moved to common node of the config.

@kurkle I remember you had some concerns about that. Instead, if it' ok for you, we could try to put it in version 3 but I'd wait for PR #838 review before starting any implementation.

EDIT: "Unfortunately no workaround." is not true. You can use before and after draw hooks of version 2.2.1

zk118 commented 1 year ago

Currently, I have to do that as a workaround:

display: (ctx) => {
    const { scales: { x, y } } = ctx.chart
    return x.top - y.getPixelForValue(myData.y) >0 && y.left - x.getPixelForValue(myData.x) > 0
}

Would be nice if implemented !

stockiNail commented 1 year ago

@zk118 I make a mistake writing you that there is not any workaround. If you are using version >= 2.2.0 of the plugin, there are 2 new hooks (beforeDraw and afterDraw) where you can add your customization. I think this is also easier of your workaround, based on the scale values.

You can simply add the hooks, unclipping and clipping the chart area. See following example:

    plugins: {
      annotation: {
        annotations: {
          lbl1: {
            type: 'label',
            xValue: 1.5,
            yValue: 19.5,
            content: ['This is a huge', 'label to show and', 'unclip is needed'],
            beforeDraw: ({chart}) => Chart.helpers.unclipArea(chart.ctx),
            afterDraw: ({chart}) => Chart.helpers.clipArea(chart.ctx, chart.chartArea),
          }
        }
      }
    },

See codepen: https://codepen.io/stockinail/pen/mdGNLOM

The first label is shown even if it is out of chart area. The second one is cut.

zk118 commented 1 year ago

Thanks, I will try it !

stockiNail commented 1 year ago

Apologize but having many other things to do it, I forgot it even if I have implemented the PR to enable those hooks :(. I think this is the best workaround. Probably to have the clip option for each annotation could add complexity (related to the labels of annotations managed as sub-elements) that can be easily solved in this way (no workaround but way to do it!). Let me know if it's working as you expected.

zk118 commented 1 year ago

Looking forward to trying these hooks in the following versions, but I don't think they will suit my needs as I have annotations that are always displayed outside of the chartArea and other annotations that I have to clip and I think the afterDraw: ({chart}) => Chart.helpers.clipArea(chart.ctx, chart.chartArea)line will clip all the elements right ?

stockiNail commented 1 year ago

I think the afterDraw: ({chart}) => Chart.helpers.clipArea(chart.ctx, chart.chartArea)line will clip all the elements right ?

beforeDraw and afterDraw are invoked for each element therefore if you add these hooks to all the annotations which need to be displayed outside, it should work. Have a look to the codegen, there are 2 labels, 1 outside, 1 clipped.

The concept is that you unclip and clip before and after drawing of each single element.

zk118 commented 1 year ago

Oh I see, will work then !