I'm working on a project using ECharts to visualize a large dataset. I want to display the data as a percentage change from the base value and allow users to zoom in and out. However, I'm facing issues with maintaining the original data when zooming out. The chart only shows the filtered data, and I can't zoom out to see the full range of data.
import * as echarts from 'echarts';
const chartDom = document.getElementById('main');
const myChart = echarts.init(chartDom);
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])]);
}
function calculatePercentage(data) {
const baseValue = data[0][1];
return data.map(([date, value]) => [date, (((value - baseValue) / baseValue) * 100).toFixed(2)]);
}
const originalData = calculatePercentage(data);
export function showData() {
const option = {
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%'];
}
},
title: {
left: 'center',
text: 'Large Area Chart'
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'time',
boundaryGap: false
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%']
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
rangeMode: ['value', 'value']
},
{
start: 0,
end: 100,
rangeMode: ['value', 'value']
}
],
series: [
{
name: 'Fake Data',
type: 'line',
smooth: true,
symbol: 'none',
areaStyle: {},
data: originalData
}
]
};
myChart.setOption(option);
myChart.on('dataZoom', function (params) {
const dataZoom = myChart.getOption().dataZoom[0];
const startValue = dataZoom.startValue;
const endValue = dataZoom.endValue;
const startTimestamp = new Date(startValue).getTime();
const endTimestamp = new Date(endValue).getTime();
const filteredData = data.filter(([date]) => {
const dateValue = new Date(date).getTime();
return dateValue >= startTimestamp && dateValue <= endTimestamp;
});
const percentageData = calculatePercentage(filteredData);
myChart.setOption({
series: [{
data: percentageData
}]
});
});
}
// Initialize with 1 month data
showData();
A lot of different things.
You should first set the min
and max
of the x axis to the absolute
values of your data, so the system "knows" what are the non-zoomed limits:
xAxis: {
type: 'time',
boundaryGap: false,
min: data[0][0],
max: data[data.length-1][0],
},
Don't set min
and max
in the dataZoom
; since you set rangeMode
s to
value
you should use minValue
and maxValue
, but that can be done in
the zoom callback:
myChart.setOption({
series: [{
data: percentageData
}],
dataZoom:[{
type: 'inside',
startValue: percentageData[0][0],
endValue: percentageData[percentageData.length-1][0],
rangeMode: ['value', 'value']
},
{
type: 'slider',
startValue: percentageData[0][0],
endValue: percentageData[percentageData.length-1][0],
rangeMode: ['value', 'value']
}]
});
All code in a stack snippet:
const chartDom = document.getElementById('main');
const myChart = echarts.init(chartDom);
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])]);
}
function calculatePercentage(data) {
const baseValue = data[0][1];
return data.map(([date, value]) => [date, (((value - baseValue) / baseValue) * 100).toFixed(2)]);
}
const originalData = calculatePercentage(data);
function showData() {
const option = {
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%'];
}
},
title: {
left: 'center',
text: 'Large Area Chart'
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'time',
boundaryGap: false,
min: data[0][0],
max: data[data.length-1][0],
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%']
},
dataZoom: [
{
type: 'inside',
rangeMode: ['value', 'value']
},
{
type: 'slider',
rangeMode: ['value', 'value']
}
],
series: [
{
name: 'Fake Data',
type: 'line',
smooth: true,
symbol: 'none',
areaStyle: {},
data: originalData
}
]
};
myChart.setOption(option);
myChart.on('dataZoom', function (params) {
const dataZoom = myChart.getOption().dataZoom[0];
const startValue = dataZoom.startValue;
const endValue = dataZoom.endValue;
const startTimestamp = new Date(startValue).getTime();
const endTimestamp = new Date(endValue).getTime();
const filteredData = data.filter(([date]) => {
const dateValue = new Date(date).getTime();
return dateValue >= startTimestamp && dateValue <= endTimestamp;
});
const percentageData = calculatePercentage(filteredData);
myChart.setOption({
series: [{
data: percentageData
}],
dataZoom:[{
type: 'inside',
startValue: percentageData[0][0],
endValue: percentageData[percentageData.length-1][0],
rangeMode: ['value', 'value']
},
{
type: 'slider',
startValue: percentageData[0][0],
endValue: percentageData[percentageData.length-1][0],
rangeMode: ['value', 'value']
}]
});
});
}
showData();
<div id="main" style="height:300px"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.6.0/echarts.common.min.js"></script>