chartjs / chartjs-plugin-datalabels

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

Collision #72

Open EmilMoe opened 6 years ago

EmilMoe commented 6 years ago

I am using datalabels on my bubble chart, but sometimes the bubbles and datalabels collide. Is there any solution to fix collision or detect when they are overlapping?

I can see that in the path chart > $datalabels > labels > [1] > [..n] > Label > _hitbox > _rect I have this

cx: 434.368
cy: 74.09599999999998
x0: 388.0193671875
x1: 481.7166328125
y0: 58.69599999999998
y1: 90.49599999999998

Should I check if there's something overlapping there?

update

I can see a problem with this approach as the _rect isn't updated until there has been a mouse over.

simonbrunel commented 6 years ago

Isn't it the same issue as #49 and #55 but for bubble charts? If not, can you provide a jsfiddle that showcases your issue. Anyway, $datalabels and everything prefixed by _ is private and should not be accessed in production code since it could break without notice in upcoming minor/patch releases.

EmilMoe commented 6 years ago

Thanks. Looks like same issues but I see no solutions though. Will try to find the properties from a variable without _ in front, but I don't know if that's possible. Also I need to figure out how to update the labels location.

Will be happy to share a jsfiddle later, but for now it's integrated in a vue component so a lot of extra code around it.

EmilMoe commented 6 years ago

I have been looking and seems to be nearly no public API then I can access?

This is how far I am in the code

detectCollisions(context) {
                if (this.hasCheckedForCollisions)
                    return

                let rectangles = []

                context.chart.$datalabels.labels[0].forEach(label => {
                    if (label._hitbox._rect) {
                        rectangles.push(this.getBoundaries(label._hitbox._rect, label.$context.dataset.data[label.$context.dataIndex].r))
                        this.hasCheckedForCollisions = true
                    }
                })

                if (this.hasCheckedForCollisions)
                    this.checkAllRectangles(rectangles)
            },
            getBoundaries(rectangle, importance) {
                return {
                    x: rectangle.x0,
                    y: rectangle.y0,
                    w: rectangle.x1 - rectangle.x0,
                    h: rectangle.y1 - rectangle.y0,
                    i: importance,
                }
            },
            checkAllRectangles(rectangles) {
                rectangles.forEach((r1, i1) => {
                    if (i1 === rectangles.length - 1)
                        return

                    rectangles.forEach((r2, i2) => {
                        if (this.isEqualRectangles(r1, r2))
                            return

                    if (this.isColliding(r1, r2))
                        this.handleCollision(r1, r2)
                    })
                })
            },
            isColliding(rect1, rect2) {
                if (rect1.x < rect2.x + rect2.w &&
                    rect1.x + rect1.w > rect2.x &&
                    rect1.y < rect2.y + rect2.h &&
                    rect1.h + rect1.y > rect2.y)
                    return true

                return false
            },
            isEqualRectangles(rect1, rect2) {
                return rect1.x === rect2.x && rect1.y === rect2.y && rect1.h === rect2.h && rect1.w === rect2.w
            },
            handleCollision(rect1, rect2) {
                console.log(rect1)
                rect1.y = rect1.h + rect2.y
                console.log(rect1)
            }
simonbrunel commented 6 years ago

I have been looking and seems to be nearly no public API then I can access?

No because the plugin is still in beta and I don't want to commit on implementation details. I'm currently rewriting the whole layouting part and moved the _hitbox outside the label so your previous implementation will break at the next release.

Anyway, I'm currently looking to introduce a new option to prevent labels to overlap and I'm wondering what is your expected behavior when 2 labels overlap?

krosskawasaki commented 3 years ago

I have been looking and seems to be nearly no public API then I can access?

No because the plugin is still in beta and I don't want to commit on implementation details. I'm currently rewriting the whole layouting part and moved the _hitbox outside the label so your previous implementation will break at the next release.

Anyway, I'm currently looking to introduce a new option to prevent labels to overlap and I'm wondering what is your expected behavior when 2 labels overlap?

A good solution is what Amcharts does... image

naftis commented 2 years ago

I'm having the same issue and while I find generic or D3 solutions (here for example), they are difficult to implement for Chart.js as the algorithms aren't simple.

I'm using a bubble chart and it's becoming an increasingly worse issue while the amount of our data is getting bigger. Do we still have any kind of solution for this?

image

EmilMoe commented 2 years ago

I'm having the same issue and while I find generic or D3 solutions (here for example), they are difficult to implement for Chart.js as the algorithms aren't simple.

I'm using a bubble chart and it's becoming an increasingly worse issue while the amount of our data is getting bigger. Do we still have any kind of solution for this?

image

I switched to apex charts as they are more modern, maybe it's fixed the