I'm building a Echart (5.5.1)'s chart with a time type xAxis
and while I love the fact that it adapts the labels according to the range span, sometimes when you zoom for example it can be confusing to understand what is the time period you're actually looking at.
Therefore I added the max and min labels so I can have some visible landmarks like so:
xAxis: {
type: 'time',
data: [],
axisLabel: {
showMinLabel: true,
showMaxLabel: true,
rotate: 45
}
},
But the labels of the min and max are, somehow, even more precise than the others when I actually want the opposite. It's showing the hour & time when I want something more like day & month.
Then I tried formatting specifically the min and max label
xAxis: {
type: 'time',
data: [],
axisLabel: {
showMinLabel: true,
showMaxLabel: true,
rotate: 45
},
min: function (value) {
formatEdgeLabel(value.min)
},
max: function (value) {
formatEdgeLabel(value.max)
}
},
The function formatEdgeLabel()
returns a string of the date formatted like 23/10
, that's it. But somehow, it seems like echarts doesn't care and still show the hour time like in the screenshot above.
Does anyone know a solution to that issue or workaround maybe?
You are not using min / max correctly. These are for setting min and max boundaries on your axis. What you are looking for is the label formatter.
Examples:
// format string
axisLabel: {
formatter: '{d}/{M}/{yyyy}'
}
// function
axisLabel: {
formatter: function (value, index) {
const date = new Date(value);
if (date.getHours() === 0) {
return '{d}/{M}/{yyyy}'
}
...
return 'anything you want';
}
}
EDIT:
Overwriting the formatter behaviour only for the min and max value is not supported by echarts and poses a few difficulties.
To address the first issue I came up with a scrappy solution:
let base = +new Date(1988, 9, 3);
let oneDay = 24 * 3600 * 1000;
let data = [[base, Math.random() * 300]];
for (let i = 1; i < 20000; i++) {
let now = new Date((base += oneDay));
data.push([+now, Math.round((Math.random() - 0.5) * 20 + data[i - 1][1])]);
}
option = {
xAxis: {
type: 'time',
boundaryGap: false,
axisLabel: {
formatter: function (value, index) {
let boundaryLabel = false;
if (index === 0 || value === endValue) {
boundaryLabel = true;
if (value === endValue) maxIndex = index;
}
if (tickSpan < 1000) { // one second
return boundaryLabel ? '{dd}-{MM}-{yyyy} {hh}:{mm}:{ss} {SSS}' : '{mm}:{ss} {SSS}';
} else if (tickSpan < 1000 * 60) { // one minute
return boundaryLabel ? '{dd}-{MM}-{yyyy} {hh}:{mm}:{ss}' : '{mm}:{ss}';
} else if (tickSpan < 1000 * 60 * 60) { // one hour
return boundaryLabel ? '{HH}:{mm}' : '{dd}-{MM}-{yyyy} {hh}:{mm}'
} else if (tickSpan < 1000 * 60 * 60 * 24) { // one day
if (new Date(value).getHours() === 0) return '{dd}/{MM}/{yyyy} {hh}:{mm}';
return boundaryLabel ? '{dd}/{MM}/{yyyy} {hh}:{mm}' : '{HH}:{mm}';
} else if (tickSpan < 1000 * 60 * 60 * 24 * 30) { // one month
if (new Date(value).getDate() === 1) return '{MMM}';
return boundaryLabel ? '{d}. {MMM} {yyyy}' : '{d}'
} else if (tickSpan < 1000 * 60 * 60 * 24 * 365) { // one year
if (new Date(value).getMonth() === 0) return '{yyyy}';
return boundaryLabel ? '{MMM} {yyyy}' : '{MMM}';
} else {
return boundaryLabel ? '{MMM} {yyyy}' : '{yyyy}';
}
},
showMinLabel: true,
showMaxLabel: true
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%']
},
dataZoom: [
{
type: 'inside',
},
{
realtime: false
}
],
series: [
{
type: 'line',
smooth: true,
symbol: 'none',
areaStyle: {},
data: data
}
]
};
const minX = data[0][0];
const maxX = data[data.length-1][0];
const dataSpan = maxX - minX;
let endValue = maxX;
let maxIndex = 6;
let tickSpan = dataSpan / maxIndex;
myChart.on('dataZoom', function (params) {
if (params.from === undefined && params.batch === undefined) return;
const end = params.end !== undefined ? params.end : params.batch[0].end;
const start = params.start !== undefined ? params.start : params.batch[0].start;
endValue = Math.round(dataSpan * end * 0.01 + minX);
const zoomSpan = endValue - Math.round(dataSpan * start * 0.01 + minX);
tickSpan = zoomSpan / maxIndex;
myChart.dispatchAction({
type: 'dataZoom',
start: params.start,
end: params.end
});
});
The min value can be determined by the index 0. To determine the max value, I am listening to the dataZoom event and computing the expected maximum. Since the event is triggered only after the axis labels have been updated, I trigger the event again. Now the computed maximum can be used to update the labels.
For hand formatting the other labels, the default formatting strategy of echarts is mentioned in the formatter documentation under 'Cascading Templates'. I included an example to give you an idea how it can be done that you can improve on.