Skip to content

fix: xAxis label overflow fixed when containLabel=true #16880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

jiawulin001
Copy link
Member

@jiawulin001 jiawulin001 commented Apr 13, 2022

Brief Information

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

Fix the problem of xAxis label overflow when grid.contailLabel is true.

Fixed issues

Details

Before: What was the problem?

As is described in #16875 (comment), overflow of x-axis label is not considered when applying grid.containLabel.

before#16875

After: How is it fixed in this PR?

Judgements whether the label will exceed boundary are added and grid will be adjusted accordingly.

after#16875

Misc

  • The API has been changed (apache/echarts-doc#xxx).
  • This PR depends on ZRender changes (ecomfe/zrender#xxx).

Related test cases or examples to use the new APIs

Attached

Merging options

  • Please squash the commits into a single one when merging.

@echarts-bot
Copy link

echarts-bot bot commented Apr 13, 2022

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

@pissang pissang added this to the 5.3.3 milestone Apr 14, 2022
@@ -202,10 +200,32 @@ class Grid implements CoordinateSystemMaster {
}
}
});

//Adjust grid.width to keep xAxis labels in dom
const [xAxis, yAxis] = axesList[0].isHorizontal() ? axesList : axesList.slice().reverse();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need complex logic to check the first label and last label here.

In the

gridRect[dim] -= labelUnionRect[dim] + margin;
we only adjust x/width based on the labels of yAxis. We can also check if the union rect of xAxis labels excced the canvas size. Then adjust x/width to avoid labels overflow.

Copy link
Member Author

@jiawulin001 jiawulin001 Apr 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function labelUnionRect actually returns the largest bounding box of all labels of an axis, not the union bounding box. So I am not sure how to get the union rect of xAxis with this function. Also it would be hard to get the real size of displayed bounding box of labels because echarts seems to automatically clip labels according to axis length. That's why we just need to make sure the first/last label does not exceed the dom. Please correct me if I misunderstand you.

Copy link
Contributor

@pissang pissang Apr 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function labelUnionRect actually returns the largest bounding box of all labels of an axis, not the union bounding box

The method estimateLabelUnionRect returns the union bounding box of all labels, not the largest.

so it would be hard to get the real size of displayed bounding box of labels because echarts seems to automatically clip labels according to axis length

I'm not sure what do you mean by clip labels. Correct me if I'm wrong. Indeed we only pick some of the labels if there are amount of theme, especially in category axis.

if (tickCount > 40) {

Perhaps the last label will also be skipped. We can add some extra logic to ensure the first label and last label are also included.

Copy link
Contributor

@pissang pissang Apr 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the current logic of estimating interval and get formatted labels seems has been capsulated in the method

getViewLabels(): ReturnType<typeof createAxisLabels>['labels'] {

Using this method to get all formatted labels will be more accurate and performant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method estimateLabelUnionRect returns the union bounding box of all labels, not the largest.

The logic of function rect.union is returning the union of bounding rect.
But here in echarts, if you trace the dataflow, every singleRect has {x: 0, y: 0}. So when you use union on two rects at the same position, you are just returning the larger one. That's why I say it actually returns the largest bounding rect of all labels.

for (let i = 0; i < tickCount; i += step) {
const tick = realNumberScaleTicks
? realNumberScaleTicks[i]
: {
value: categoryScaleExtent[0] + i
};
const label = labelFormatter(tick, i);
const unrotatedSingleRect = axisLabelModel.getTextRect(label);
const singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
rect ? rect.union(singleRect) : (rect = singleRect);
}

This doesn't result in errors for now because only x/height and y/width is used in older version, and they are the same for union rect and largest rect.

However, thanks to your suggestion, I double-checked Axis.ts and found the function getTicksCoords, which returns all displayed labels' (or ticks', they bind each other) coordinates. So I rewrite the changes and now I can accurately grab the first and the last label and check whether they would exceed the dom. Please review again.

P.S. Do you mind telling me how to get the dom size in Echarts? I struggled to get dom attributes but failed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what I want to emphasize is right-most DISPLAYED label. The function getTicksCoord would only return the labels that would be DISPLAYED. Those which are automatically hidden will not be included so I can just call getTicksCoord()[0] to get the left-most DISPLAYED label and the right-most alike.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, xAxis.inverse is also considered. For example, left-most label will be treated as the right-most if xAxis.inverse = true.

Copy link
Contributor

@pissang pissang Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what I want to emphasize is right-most DISPLAYED label. The function getTicksCoord would only return the labels that would be DISPLAYED. Those which are automatically hidden will not be included so I can just call getTicksCoord()[0] to get the left-most DISPLAYED label and the right-most alike.

I think that's not the difference. The union bounding rect should also be from the displayed labels .

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, do you mean use getTicksCoord to find displayed labels index, find their bounding box and implement union bounding rects on them? I don't see why we need to do that. Are you worrying that some too long in-middle label would exceed the dom too?

Copy link
Contributor

@pissang pissang Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getViewLabels will return the labels that will be finally displayed on the chart. See how it is used when rendering the axis.
https://github.com/apache/echarts/blob/master/src/component/axis/AxisBuilder.ts

However the getTicksCoord will have a different result when user-set axisTick.interval and axisLabel.interval are different. getTicksCoord will use the axisTick.interval to skip the tick lines and getViewLabels will use axisLabel.interval to skip the coord.

image

@jiawulin001
Copy link
Member Author

However the getTicksCoord will have a different result when user-set axisTick.interval and axisLabel.interval are different.

That's a good point. I rewrite the method with getViewsLabel and use their bounding rect to find the left and right exceeds. Please check it out.

@jiawulin001
Copy link
Member Author

Just updated the code and delete the "simple optimization" of step. When step is larger than 1, It changes the number of label rects returned from function estimateLabelRect so getViewsLabel().tickvalue would not be able to catch the right label rect. Worse still, the number is usually cut by half so tickvalue will exceed the extent of the rect array and result in render error.

@jiawulin001
Copy link
Member Author

Just update another fix to check the axis type before using tickValue to find the corresponding label rect. If axis type is value, tickValue would be the exact value at the tick so it would almost always exceed the extent of array of label rects. In that case, the index of the tick is used instead because axisLabel.interval is no longer active so index of tick and label would be sync.

@Ovilia Ovilia modified the milestones: 5.3.3, 5.4 Jun 10, 2022
@Ovilia Ovilia modified the milestones: 5.4, 5.4.1 Sep 1, 2022
@pissang pissang modified the milestones: 5.4.1, 5.5.0 Nov 12, 2022
@MrSquaare
Copy link

Hello, what's the state of this PR? Thanks 🙂

@Ovilia Ovilia removed this from the 5.5.0 milestone Jan 18, 2024
@Ovilia Ovilia added this to the 5.5.1 milestone Jan 18, 2024
@Ovilia Ovilia modified the milestones: 5.5.1, 5.5.2 Jun 28, 2024
@plainheart plainheart modified the milestones: 5.5.2, 6.0.0 Jul 18, 2024
@tracyfarah
Copy link

any updates on this?

@Ovilia Ovilia modified the milestones: 6.0.0, 6.x Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug] Grid.containLabel is set to true, but axisLabel is still truncated
6 participants