Setting custom min/max values on the yAxis for bar charts, makes the bars go below the lowest limit of x axis and above the highest limit of y axis.
Without min/max on bar chart I get,
When I set min/max on bar chart I get,
I do not face this issue if I change the chart type to line,
Line without min/max:
Line with min/max:
What can I do to make min/max for bar behave like line ? Is there some way I can prevent the bars from going outside limits ?
option = {
xAxis: {
type: 'category',
data: ['Foo', 'Bar', 'Baz']
},
yAxis: {
type: 'value',
min: 200,
max: 250
},
series: [{
data: [100, 200, 300],
type: 'line' // 'bar'
}]
};
Related:
If I specify min/max values outside min/max value of data, I see bars for 'bar' type graph.
option = {
xAxis: {
type: 'category',
data: ['Foo', 'Bar', 'Baz']
},
yAxis: {
type: 'value',
min: -250,
max: -150
},
series: [{
data: [100, 200, 300],
type: 'bar'
}]
};
This doesn't happen for line,
option = {
xAxis: {
type: 'category',
data: ['Foo', 'Bar', 'Baz']
},
yAxis: {
type: 'value',
min: -250,
max: -150
},
series: [{
data: [100, 200, 300],
type: 'line'
}]
};
Is there any news on this issue? For lines some sort of clipping is performed but not for bar types.
@suremicro, I am still facing the same issue. No new developments.
I know this is not a solution for everybody, but in my case is ok. What I did is hide the bar below the axis line by using the axis ticks. So I have some exaggerated axis ticks like:
axisTick: {
show: true,
alignWithLabel: true,
length: 100,
lineStyle: {
width: 500,
color: '#f3f3f3'
}
},
Axis ticks are positioned by default below the axis labels and do not mess with grid positioning which is nice. Just replace the color with the background color of your choice so it mimics the background.
I hope this issue continues opened and can have a real solution in the future!
Nice try at fixing this ;) I can't believe this hasn't been fixed as it makes an otheriwse excellent charting package almost unusable in a product.
I have a workaround to this that uses the custom chart type. Using this custom type, a bar chart style can be re-created and the in-built view clipping function used to ensure the chart doesn't exceed the chart area.
It is just a test currently and doesn't support horizontal bar styles or stacking but does partially resolve this annoying issue.
@suremicro No need to go the custom chart route. I've solved this using a similar solution to yours but instead of creating a custom chart, I update each bar's layout properties. You can update each bar's layout by registering a layout function before initializing your chart.
Example:
echarts.registerLayout(myCustomLayoutFunction);
echarts.init(myChartContainer);
function myCustomLayoutFunction(ecModel) {
ecModel.eachSeries(seriesComponent => {
const data = seriesComponent.getData();
data.each(idx => {
const layout = data.getItemLayout(idx);
if (layout) {
const newLayout = echarts.graphic.clipRectByRect(
layout,
seriesComponent.coordinateSystem.grid.getRect(),
);
if (newLayout) {
({ x: layout.x, y: layout.y, width: layout.width, height: layout.height } = newLayout);
}
}
});
}
}
I use this technique of registering layout functions for my charts quite often to do things like dynamically showing or hiding series labels depending if they fit.
Thanks Jonathan this is exactly what I was looking for, however I can't find any documentation about registerLayout. Do you need to return the new layout?
Thanks Jonathan this is exactly what I was looking for, however I can't find any documentation about registerLayout. Do you need to return the new layout?
No need to return the new layout. Just overwrite the properties of the one returned by:
data.getItemLayout(idx);
I'm using ES2015 code, so I did it above using object destructuring.
({ x: layout.x, y: layout.y, width: layout.width, height: layout.height } = newLayout)
As far as I know, the team hasn't documented all of the API. I often read the code of the existing charts to get familiar with the undocumented portions of the API.
That's what I assumed from the code, the I only asked as it doesn't seem to clip. BTW I found there's a
data.setItemLayout(index, layout);
function as well (which does them same as your line).
I expected to give clipRectByRect the data item layout along with the min/max area and it returned a clipped layout which I then apply, but no luck
I see now what I was missing - the layout seems to be pixel co-ordinates with the height neing a negative value, which is why the clipRectByRect doesn't work (the grid is all positive).
I've added a simple co-ordinate limiting logic so the bars do keep within the grid boundary when zooming and scrolling, however at the moment it does leave the labels 'stuck' to the axis lines.
Although not perfect it does look better than at present - thanks to Jonathan for the info.
function chartLayout(ec) {
ec.eachSeries(seriesComponent => {
var data = seriesComponent.getData();
data.each(idx => {
var layout = data.getItemLayout(idx);
if (layout) {
var rect = seriesComponent.coordinateSystem.grid.getRect();
if (layout.x < rect.x) {
layout.width = layout.width - (rect.x - layout.x);
layout.x = rect.x;
}
if (layout.x + layout.width > rect.x + rect.width) {
layout.width = rect.x + rect.width - layout.x;
}
if (layout.width < 0) layout.width = 0;
if (layout.y > rect.y + rect.height) {
layout.height = layout.height - (rect.y + rect.height - layout.y);
layout.y = rect.y + rect.height;
}
var absY = (rect.y + rect.height) - layout.y;
if (absY + Math.abs(layout.height) + 65 > rect.y + rect.height) {
layout.height = -(rect.y + rect.height - 65 - absY);
}
if (layout.height > 0) layout.height = 0;
}
});
});
}
@suremicro Can you post an example of the 'stuck' labels in a codepen or some other online code playground to check it out?
@jonavila Could you point me in the right direction to hide labels in bar charts? Thank you
@suremicro Here you go: https://codesandbox.io/s/echarts-showhide-labels-in-bars-jr1ks
I've adapted an example from one of my use cases, but I've simplified it a bit to only account for vertical bars with 90 rotated labels.
Here's the gist of it:
In the demo, I'm showing the labels on hover so that you can see them overflow the bars.
Please resize the window in the sandbox so that you can see lables show/hide
@jonavila That's very helpful and will also allow me to investiagate the API further myself.
Thank your for your time on this.
I'm noticing the same issue but in the X axis when I use time
type.
https://jsfiddle.net/jsilversun/10wm6dny/23/
I tried what you shared for the Y axis (the layout function) but it didn't work for me, do you have any insights about how I could fix this?
You example doesn't include the layout clipping function??? You need to add the function and register it using the global echarts function. If you try the code below in your jsfiddle example it will clip the bars ;)
echarts.registerLayout(chartLayout);
var container = document.getElementById('main');
var chart = echarts.init(container);
const min = "2020-11-25T06:10";
const max = "2020-11-25T08:10";
function getData() {
const data = [];
let now = +new Date(2020, 10, 25);
const oneDay = 10 * 60 * 1000;
const randomData = ()=> {
now = new Date(+now + oneDay);
return {
value: [
now.toISOString().substring(0,16),
Math.floor(Math.random() * 100),
],
};
};
for (let i = 0; i < 72; i++) {
data.push(randomData());
}
return data;
}
function chartLayout(ec) {
ec.eachSeries(seriesComponent => {
var data = seriesComponent.getData();
data.each(idx => {
var layout = data.getItemLayout(idx);
if (layout) {
var rect = seriesComponent.coordinateSystem.grid.getRect();
if (layout.x < rect.x) {
layout.width = layout.width - (rect.x - layout.x);
layout.x = rect.x;
}
if (layout.x + layout.width > rect.x + rect.width) {
layout.width = rect.x + rect.width - layout.x;
}
if (layout.width < 0) layout.width = 0;
if (layout.y > rect.y + rect.height) {
layout.height = layout.height - (rect.y + rect.height - layout.y);
layout.y = rect.y + rect.height;
}
var absY = (rect.y + rect.height) - layout.y;
if (absY + Math.abs(layout.height) + 65 > rect.y + rect.height) {
layout.height = -(rect.y + rect.height - 65 - absY);
}
if (layout.height > 0) layout.height = 0;
}
});
});
}
const data = getData();
chart.setOption({
xAxis: [
{
splitNumber: 24,
scale: false,
min,
max,
type: "time",
axisLabel: {
formatter(value) {
return moment(value).format("HH");
}
}
}
],
yAxis: [
{
type: "value"
}
],
series: [
{
data,
type:'bar',
}
]
});
Most helpful comment
@suremicro No need to go the custom chart route. I've solved this using a similar solution to yours but instead of creating a custom chart, I update each bar's layout properties. You can update each bar's layout by registering a layout function before initializing your chart.
Example:
I use this technique of registering layout functions for my charts quite often to do things like dynamically showing or hiding series labels depending if they fit.