I want to plot the variance of multiple signals in a chart (or basically fillup the space between an upper and a lower signal).
Is it possible to create such kind of charts?
I saw the confidence-band example (https://echarts.apache.org/examples/en/editor.html?c=confidence-band) , however this seems to work only for one signal in a chart.
Another solution would be to draw thousands of small rectangles using markArea around the signals but this slows down the performance of the chart (e.g. when scrolling the x-axisis) and doesnt look very smooth.
As I know the common practice in Echarts community draw usual chart type (bar, line, ...) with series (read docs) and write visual logic by custom series for unique. Also Echarts has some API methods (undocumented) like registerVisual, registerLayout that can be used for redefine layouts, computation and so on.
For described task you need to use custom series for calculate bands coordinates. It's not very simple because (it seems to me) mandatory requirements with confidence band is rare.
About performance. Echarts by default use Canvas for render visual parts. Usually Canvas has no many parts in HTML for display chart, it's just imageData rendered by browser and it almost doesn't matter how many data point need to display. In other words, we see PNG, and not a lot of div, svg, g and others layers with geometric primitives as in SVG but heavy computation complex business logic may affect the responsiveness of UI as in other charts.
Below example how would I implement this feature. I'm not sure that's the right way but it work and can be tuned.
var dates = ['2020-01-03','2020-01-31','2020-02-17','2020-02-18','2020-03-13','2020-04-10','2020-05-01','2020-05-19','2020-05-22','2020-05-25'];
var sensor1 = [0.6482086334797242, 0.9121368038482911, 0.3205730196548609, 0.8712238348969002, 0.4487714576177558, 0.9895025457815625, 0.0415490306934774, 0.1592908349676395, 0.5356690594518069, 0.9949108727912939];
var sensor2 = [0.8278430459565170, 0.5700757488718124, 0.9803575576802187, 0.0770264671179814,0.2843735619252158,0.8140209568127250,0.6055633547296827,0.9554255125528607,0.1703504100638565,0.5653245914197297];
// Calculate fake bands coordinates
function calcContourCoords(seriesData, ctx){
var addNoise = idx => Math.round(Math.random() * 8 * idx);
var pixelCoords = seriesData.map((dataPoint, idx) => {
return [
ctx.convertToPixel({ xAxisIndex: 0 }, idx) + addNoise(idx),
ctx.convertToPixel({ yAxisIndex: 0 }, dataPoint) + addNoise(idx)
]
});
var polyfilltype = ClipperLib.PolyFillType.pftEvenOdd;
var linePath = new ClipperLib.Path();
var delta = 15;
var scale = 1;
for (var i = 0; i < pixelCoords.length; i++){
var point = new ClipperLib.IntPoint(...pixelCoords[i]);
linePath.push(point);
}
var co = new ClipperLib.ClipperOffset(1.0, 0.25);
co.AddPath(linePath, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etOpenSquare);
co.Execute(linePath, delta * scale);
return co.m_destPoly.map(c => [c.X, c.Y])
}
// Render visual by calculated coords
function renderItem(params, api){
// Prevent multiple call
if (params.context.rendered) return;
params.context.rendered = true;
// Get stored in series data for band
var series = myChart.getModel().getSeriesByName(params.seriesName)[0];
var seriesData = series.get('data');
// Calculate band coordinates for series
var bandCoords = calcContourCoords(seriesData, myChart);
// Draw band
return {
type: 'polygon',
shape: {
points: echarts.graphic.clipPointsByRect(bandCoords, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
})
},
style: api.style({
fill: series.option.itemStyle.color
})
};
}
// =============
var option = {
tooltip: {},
legend: {
data:['Label']
},
xAxis: [
{ name: 'x0', data: dates, boundaryGap: true },
{ name: 'x1', data: dates, boundaryGap: true, show: false },
],
yAxis: [
{ name: 'y0' },
{ name: 'y1', show: false },
],
series: [
// First line
{
name: 'Sensor1',
type: 'line',
data: sensor1,
itemStyle: { color: 'rgba(69, 170, 242, 1)' },
yAxisIndex: 0,
xAxisIndex: 0,
},
{
name: 'BandSensor1',
type: 'custom',
data: sensor1,
itemStyle: { color: 'rgba(69, 170, 242, 0.2)' },
renderItem: renderItem,
yAxisIndex: 0,
xAxisIndex: 0,
},
// Second line
{
name: 'Sensor2',
type: 'line',
data: sensor2,
itemStyle: { color: 'rgba(253, 151, 68, 1)' },
yAxisIndex: 1,
xAxisIndex: 1,
},
{
name: 'BandSensor2',
type: 'custom',
data: sensor2,
itemStyle: { color: 'rgba(253, 151, 68, 0.2)' },
renderItem: renderItem,
yAxisIndex: 1,
xAxisIndex: 1,
},
]
};
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option);
<script src="https://cdn.jsdelivr.net/npm/clipper-lib#6.4.2/clipper.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.7.0/echarts.min.js"></script>
<div id="main" style="width: 800px;height:600px;"></div>
Improved version of #Sergey Fedorov- this solution takes into account min,max values or. dynamic border thicness of the band
var dates = ['2020-01-03', '2020-01-31', '2020-02-17', '2020-02-18', '2020-03-13', '2020-04-10', '2020-05-01', '2020-05-19', '2020-05-22', '2020-05-25', '2020-05-27'];
const data_raw1 = [
{ min: -5, mean: 0, max: 0 },
{ min: 1, mean: 2, max: 5 },
{ min: 2, mean: 4, max: 6 },
{ min: 4, mean: 5, max: 8 },
{ min: 7, mean: 11, max: 14 },
{ min: 11, mean: 15, max: 17 },
{ min: 6, mean: 8, max: 8.5 },
{ min: -1, mean: 5, max: 6 },
{ min: 4, mean: 9, max: 12 },
{ min: 14, mean: 18, max: 22 },
{ min: 18, mean: 20, max: 21 },
];
const data_raw2 = [
{ min: 10, mean: 15, max: 20 },
{ min: 12, mean: 25, max: 30 },
{ min: 22, mean: 26, max: 32 },
{ min: 30, mean: 31, max: 45 },
{ min: 47, mean: 49, max: 50 },
{ min: 30, mean: 32, max: 41 },
{ min: 34, mean: 36, max: 38 },
{ min: 40, mean: 42, max: 45 },
{ min: 47, mean: 49, max: 56 },
{ min: 60, mean: 68, max: 70 },
{ min: 75, mean: 80, max: 85 },
];
const data_raw3 = data_raw2.map(d => ({ min: d.min * 1.2 + 10, mean: d.mean * 1.4 + 11, max: d.max * 1.5 + 12 }))
function calcContourCoords(seriesData, ctx) {
console.log("seriesData=", seriesData);
const pixelCoords = []
for (let i = 0; i < seriesData.length; i++) {
console.log(i, seriesData[i]);
pixelCoords.push([
ctx.convertToPixel({ xAxisIndex: 0 }, i),
ctx.convertToPixel({ yAxisIndex: 0 }, seriesData[i].max)
]);
}
console.log("\n")
for (let i = seriesData.length - 1; i >= 0; i--) {
console.log(i, seriesData[i]);
pixelCoords.push([
ctx.convertToPixel({ xAxisIndex: 0 }, i),
ctx.convertToPixel({ yAxisIndex: 0 }, seriesData[i].min)
]);
if (i == 0) {
pixelCoords.push([
ctx.convertToPixel({ xAxisIndex: 0 }, i),
ctx.convertToPixel({ yAxisIndex: 0 }, seriesData[i].max)
]);
}
}
var linePath = new ClipperLib.Path();
var delta = 10;
var scale = 1;
for (var i = 0; i < pixelCoords.length; i++) {
var point = new ClipperLib.IntPoint(...pixelCoords[i]);
linePath.push(point);
}
var co = new ClipperLib.ClipperOffset(1.0, 0.25);
co.AddPath(linePath, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
co.Execute(linePath, delta * scale);
return co.m_destPoly.map(c => [c.X, c.Y])
}
// Render visual by calculated coords
function renderItem(params, api) {
// Prevent multiple call
if (params.context.rendered) return;
params.context.rendered = true;
// Get stored in series data for band
var series = myChart.getModel().getSeriesByName(params.seriesName)[0];
var seriesData = series.get('data');
// Calculate band coordinates for series
var bandCoords = calcContourCoords(seriesData, myChart);
// Draw band
return {
type: 'polygon',
shape: {
points: echarts.graphic.clipPointsByRect(bandCoords, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
})
},
style: api.style({
fill: series.option.itemStyle.color
})
};
}
// =============
var option = {
tooltip: {},
legend: {
data: ['Label']
},
xAxis: [
{ name: 'x0', data: dates, boundaryGap: true },
{ name: 'x1', data: dates, boundaryGap: true, show: false },
],
yAxis: [
{ name: 'y0' },
{ name: 'y1', show: false },
],
series: [
// First line
{
name: 'Sensor1',
type: 'line',
data: data_raw1.map(d => d.mean),
itemStyle: { color: 'rgba(69, 170, 242, 1)' },
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'red' },
},
{
name: 'BandSensor1',
type: 'custom',
data: data_raw1,
itemStyle: { color: 'rgba(69, 170, 242, 0.2)' },
renderItem: renderItem,
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'red', opacity: 0.1 },
},
{
name: 'Sensor2',
type: 'line',
data: data_raw2.map(d => d.mean),
itemStyle: { color: 'rgba(69, 170, 242, 1)' },
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'blue' },
},
{
name: 'BandSensor2',
type: 'custom',
data: data_raw2,
itemStyle: { color: 'rgba(69, 170, 242, 0.2)' },
renderItem: renderItem,
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'blue', opacity: 0.1 },
},
{
name: 'Sensor3',
type: 'line',
data: data_raw3.map(d => d.mean),
itemStyle: { color: 'rgba(69, 170, 242, 1)' },
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'green' },
},
{
name: 'BandSensor3',
type: 'custom',
data: data_raw3,
itemStyle: { color: 'rgba(69, 170, 242, 0.2)' },
renderItem: renderItem,
yAxisIndex: 0,
xAxisIndex: 0,
itemStyle: { color: 'green', opacity: 0.1 },
},
]
};
var myChart = echarts.init(document.getElementById('chart'));
myChart.setOption(option);
Related
I have two line charts in a grid so that I can have common zooming. Now, I want to have tooltip such that when I hover on one chart, tooltip popups in other as well. So, I can see, how the values are in both charts at the specific x-axis value.
Below is my code,
let base = +new Date(1968, 9, 3);
let oneDay = 24 * 3600 * 1000;
let date = [];
let values1 = [Math.random() * 300];
let values2 = [Math.random() * 300];
for (let i = 1; i < 20000; i++) {
var now = new Date((base += oneDay));
date.push(i)
// date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
values1.push(Math.round((Math.random() - 0.5) * 20 + values1[i - 1]));
values2.push(Math.round((Math.random() - 0.5) * 20 + values2[i - 1]));
}
option = {
// tooltip: {
// trigger: 'none',
// // position: function (pt) {
// // return [pt[0], '10%'];
// // }
// },
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
grid: [
{
// left: '3%',
// right: '4%',
top: '50%',
containLabel: true,
// show: true
},
{
// left: '3%',
// right: '4%',
bottom: '50%',
containLabel: true,
// show: true
},
],
xAxis: [
{
type: 'category',
boundaryGap: false,
data: date,
gridIndex: 0,
},
{
type: 'category',
boundaryGap: false,
data: date,
gridIndex: 1,
},
],
yAxis: [
{
type: 'value',
boundaryGap: [0, '100%'],
gridIndex: 0,
},
{
type: 'value',
boundaryGap: [0, '100%'],
gridIndex: 1,
},
],
dataZoom: [
{
type: 'inside',
// start: 0,
// end: 10
xAxisIndex: [0, 1],
},
// {
// // start: 0,
// // end: 10
// xAxisIndex: [0, 1],
// }
],
series: [
{
name: 'Fake Data',
type: 'line',
symbol: 'none',
sampling: 'lttb',
itemStyle: {
color: 'rgb(255, 70, 131)'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(255, 158, 68)'
},
{
offset: 1,
color: 'rgb(255, 70, 131)'
}
])
},
data: values1,
gridIndex: 0,
xAxisIndex: 0,
yAxisIndex: 0,
},
{
name: 'Faker Data',
type: 'line',
symbol: 'none',
sampling: 'lttb',
itemStyle: {
color: 'rgb(255, 70, 131)'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(255, 158, 68)'
},
{
offset: 1,
color: 'rgb(255, 70, 131)'
}
])
},
data: values2,
gridIndex: 1,
xAxisIndex: 1,
yAxisIndex: 1,
}
]
};
The above code will render 2 charts with common zooming. I tried tooltip with item, axes and none but they didnt work.
How can I do this?
Thank you.
Solution 1
First, when using this kind of chart (with xAxis), it's recommanded to use trigger: "axis".
Then to connect the tooltip, what you actually want to do is to connect the axisPointer :
// add this to your chart option
axisPointer: {
link: [
{
xAxisIndex: 'all'
}
]
},
This being done, your 2 tooltips will be connected. But echart seems to struggle to display the tooltip on large dataset using xAxis.type = 'category' (20K points in your example). If you want to use big dataset with 'date' format on x, I recommand this :
Format you series like a list of [date, value]
Use xAxis.type = 'time'
Remove xAxis.data
Here is the full working code of your example.
Solution 2
Another solution would be to create 2 separate charts and to connect them (zoom, tooltip ...) using
echarts.connect([chart1, chart2]);
Here is what the code would look like with this method :
var myChart1 = echarts.init(document.getElementById('main1'));
var myChart2 = echarts.init(document.getElementById('main2'));
let base = +new Date(1968, 9, 3);
let oneDay = 24 * 3600 * 1000;
let date = [];
let values1 = [Math.random() * 300];
let values2 = [Math.random() * 300];
for (let i = 1; i < 200; i++) {
var now = new Date((base += oneDay));
date.push(i)
// date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
values1.push(Math.round((Math.random() - 0.5) * 20 + values1[i - 1]));
values2.push(Math.round((Math.random() - 0.5) * 20 + values2[i - 1]));
}
var option1 = {
tooltip: {
trigger: 'axis',
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: date
}
],
yAxis: [
{
type: 'value',
boundaryGap: [0, '100%'],
},
],
dataZoom: [
{
type: 'inside',
},
],
series: [
{
name: 'Fake Data',
type: 'line',
symbol: 'none',
sampling: 'lttb',
itemStyle: {
color: 'rgb(255, 70, 131)'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(255, 158, 68)'
},
{
offset: 1,
color: 'rgb(255, 70, 131)'
}
])
},
data: values1,
},
]
};
var option2 = {
tooltip: {
trigger: 'axis',
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: date
}
],
yAxis: [
{
type: 'value',
boundaryGap: [0, '100%'],
},
],
dataZoom: [
{
type: 'inside',
},
],
series: [
{
name: 'Faker Data',
type: 'line',
symbol: 'none',
sampling: 'lttb',
itemStyle: {
color: 'rgb(255, 70, 131)'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(255, 158, 68)'
},
{
offset: 1,
color: 'rgb(255, 70, 131)'
}
])
},
data: values2,
},
]
};
myChart1.setOption(option1)
myChart2.setOption(option2)
echarts.connect([myChart1, myChart2]);
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.3.2/echarts.min.js"></script>
<div id="main1" style="width: 600px; height:200px;"></div>
<div id="main2" style="width: 600px; height:200px;"></div>
</body>
</html>
I have a mixed chart (scatter and line), hence I need 2 xAxis.
There's an extra grid that appears on the bottom of the chart, because that's where the other x axis normally has its labels (I removed them because I don't need them)
I'm trying to get rid of that extra grid, but I can't find how to do it. I've tried to specify the ticks color as white but the grid's still there.
Look at the image, it's that solitary extra grid at the bottom (marked with an horizontal bracket):
This is my code:
const ctx = document.getElementById('myChart');
const labels1 = ['A','R','Fyo','A-MAC','HR','Do','Rs','Dpr','GM','GF','EPK','EPS','Is1','Is2','Is3','FP','INVAR','INVER'];
const lasIs = (ctx, value) => ctx.p0DataIndex > 11 ? value: undefined;
const markedIdxs = [0,5,10,15,20,25,30,35,44, //... and many more];
const markedVals = [0,5,10,15,20,25,30,35,10,// ... and many more];
const data1 = {
labels: labels1,
datasets: [
{
type: 'line',
label: 'Line Dataset',
data: [65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65],
pointRadius: 0,
borderWidth: 1,
borderColor: '#0070C5',
xAxisID: 'x2'
},
{
type: 'line',
label: 'Line Dataset',
data: [50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50],
pointRadius: 0,
borderWidth: 1,
borderColor: '#0070C5',
xAxisID: 'x2'
},
{
type: 'line',
label: 'Line Dataset',
data: [102, 38, 54, 56, 102, 38, 54, 56, 102, 38, 54, 56, 102, 38, 54, 87, 62, 91],
pointStyle: 'circle',
pointRadius: 5,
borderWidth: 2,
borderColor: 'rgba(0, 120, 161,0.5)',
backgroundColor: 'rgba(0, 120, 161,0.5)',
fill: true,
segment: {
borderColor: ctx => lasIs (ctx, 'rgba(0, 168, 172, 0.5'),
backgroundColor: ctx => lasIs (ctx, 'rgba(0, 168, 172, 0.5'),
},
xAxisID: 'x2'
},
{
type: 'scatter',
pointStyle: 'dash',
pointRadius: 6,
borderColor: 'rgba(0, 0, 255,0.2)',
xAxisID: 'x',
data: [{x:1, y:36}, {x:1, y:37}, {x:1, y:39}, {x:1, y:40}, {x:1, y:42}... //and many more]
},
],
};
const multiplos5 = {
id: 'multiplos5',
afterDatasetsDraw(chart, args, options) {
const {ctx} = chart;
ctx.save();
ctx.font = "8px Verdana";
ctx.fillStyle = "#0070C5";
yaSeDibujo=84;
paddLeft = 0;
for (i=0; i < markedIdxs.length; i++) {
if (markedVals[i] < 10)
paddLeft=8;
else
paddLeft=12;
ctx.fillText(markedVals[i], chart.getDatasetMeta(3).data[markedIdxs[i]].x - paddLeft, chart.getDatasetMeta(3).data[markedIdxs[i]].y + 3);
}
}
};
Chart.register(multiplos5);
const myChart = new Chart(ctx, {
data: data1,
options: {
scales: {
x2: { // add extra axes
min: 0,
max: 18,
position: 'bottom',
type: 'category',
ticks: {color: '#0070C5'},
offset: true
},
x: {
min: 1,
max: 18,
ticks: {stepSize: 1, display: false},
offset: true
},
y: {
min: 20,
max: 120,
ticks: {stepSize: 10, color: '#555555', crossAlign: 'start'},
grid:{color:'#f1f1f1', display:true},
},
y2: {
position: 'right',
min: 20,
max: 120,
ticks: {stepSize: 10, color: '#555555'},
grid:{display:false}
},
},
plugins: {
multiplos5,
legend: false,
tooltip: {
displayColors: false,
filter: function (tooltipItem) {
return tooltipItem.datasetIndex == 2;
},
callbacks: {
//title: "el título",
label: function(context) {
let label = labels2[context.dataIndex] + ': ' + context.parsed.y;
return label;
}
}
}
},
},
});
Chart.js version is 3.6.2
Any help/workaround will be really appreciated.
Regards!!
Forgot to mention that the axis I'm talking about is 'x' (xAxisID: 'x')
In your config for the X axis where you set the ticks to display false, if you instead do this in the root of the X scale config it will hide that line so you will get:
scales:{
x: {
display: false,
min: 1,
max: 18,
offset: true
}
}
I have the following Apache EChart:
var option = {
series: [
{
name: 'RollPitch',
type: 'gauge',
data: [
{
value: 0,
name: '',
itemStyle: { color: '#CF4437' },
},
],
min: 0,
max: 360,
splitNumber: 4,
splitLine: {
show: false,
},
startAngle: 0,
endAngle: 359.9999999,
axisLine: {
lineStyle: {
color: [[100, '#D8D8D8']],
width: 50,
},
},
axisTick: {
show: false,
},
pointer: {
show: true,
length: '110%',
width: 8,
},
detail: {
show: false,
},
},
],
};
https://echarts.apache.org/examples/en/editor.html?
What I want to achieve is to draw a circle given with x and y coordinates.
Can somebody give me a hint how a possible solution would be achieved?
Shall I draw on the canvas that is created by ECharts? How to map the position?
To draw a shape you can with the two ways:
The custom series (too expensive and complex for this case)
With the graphic component (more suitable option)
In general you need to add the component then setup predefined shape circle and you will have small circle.
var option = {
graphic: [{
elements: [{
id: 'small_circle',
type: 'circle',
z: 100,
shape: {
cx: 350,
cy: 200,
r: 20,
},
style: {
fill: 'rgba(0, 140, 250, 0.5)',
stroke: 'rgba(0, 50, 150, 0.5)',
lineWidth: 2,
}
}]
}]
// series: [...]
}
Bonus: how to update circle coordinates:
var myChart = echarts.init(document.getElementById('main'));
var option = {
graphic: [{
elements: [{
id: 'small_circle',
type: 'circle',
z: 100,
shape: {
// "... draw a circle given with x and y coordinates." — it's here
cx: 350,
cy: 200,
r: 20,
},
style: {
fill: 'rgba(0, 140, 250, 0.5)',
stroke: 'rgba(0, 50, 150, 0.5)',
lineWidth: 2,
}
}]
}],
series: [{
name: 'RollPitch',
type: 'gauge',
data: [{
value: 0,
name: '',
itemStyle: {
color: '#CF4437'
},
}, ],
min: 0,
max: 360,
splitNumber: 4,
splitLine: {
show: false,
},
startAngle: 0,
endAngle: 359.9999999,
axisLine: {
lineStyle: {
color: [
[100, '#D8D8D8']
],
width: 50,
},
},
axisTick: {
show: false,
},
pointer: {
show: true,
length: '110%',
width: 8,
},
detail: {
show: false,
},
}, ],
};
myChart.setOption(option);
/* Taken from https://stackoverflow.com/a/35455786/1597964 */
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
return [
centerX + (radius * Math.cos(angleInRadians)),
centerY + (radius * Math.sin(angleInRadians))
]
}
var angle = 90;
setInterval(function() {
var [cx, cy] = polarToCartesian(300, 200, 50, angle);
myChart.setOption({
graphic: [{
id: 'small_circle',
shape: {
cx: cx,
cy: cy,
}
}]
});
angle = angle + 1;
}, 20);
<script src="https://cdn.jsdelivr.net/npm/echarts#4.8.0/dist/echarts.min.js"></script>
<div id="main" style="width: 600px;height:400px;"></div>
I take one of the examples posted and slightly modify it by removing some data. When the sets don't have the same number of data points, the tooltips stop displaying.
Please see the following two examples. The first one being the one posted in the docs. The second, the modified version. When hovering starting from the end data points, the tooltip doesn't display anything for the last columns.
Example from docs: https://codepen.io/junedchhipa/pen/YJQKOy
var options = {
chart: {
height: 310,
type: 'line',
stacked: false,
},
stroke: {
width: [0, 2, 5],
curve: 'smooth'
},
plotOptions: {
bar: {
columnWidth: '50%'
}
},
series: [{
name: 'Series Column',
type: 'column',
data: [23, 11, 22, 27, 13, 22, 37, 21, 44, 22, 30]
}, {
name: 'Series Area',
type: 'area',
data: [44, 55, 41, 67, 22, 43, 21, 41, 56, 27, 43]
}, {
name: 'Series Line',
type: 'line',
data: [30, 25, 36, 30, 45, 35, 64, 52, 59, 36, 39]
}],
fill: {
opacity: [0.85,0.25,1],
gradient: {
inverseColors: false,
shade: 'light',
type: "vertical",
opacityFrom: 0.85,
opacityTo: 0.55,
stops: [0, 100, 100, 100]
}
},
labels: ['01/01/2003', '02/01/2003','03/01/2003','04/01/2003','05/01/2003','06/01/2003','07/01/2003','08/01/2003','09/01/2003','10/01/2003','11/01/2003'],
markers: {
size: 0
},
xaxis: {
type:'datetime'
},
yaxis: {
title: {
text: 'Points',
},
min: 0
},
legend: {
show: false
},
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function (y) {
if(typeof y !== "undefined") {
return y.toFixed(0) + " points";
}
return y;
}
}
}
}
var chart = new ApexCharts(
document.querySelector("#chart"),
options
);
chart.render();
// check if the checkbox has any unchecked item
checkLegends()
function checkLegends() {
var allLegends = document.querySelectorAll(".legend input[type='checkbox']")
for(var i = 0; i < allLegends.length; i++) {
if(!allLegends[i].checked) {
chart.toggleSeries(allLegends[i].value)
}
}
}
// toggleSeries accepts a single argument which should match the series name you're trying to toggle
function toggleSeries(checkbox) {
chart.toggleSeries(checkbox.value)
}
Modified example:
https://codepen.io/mmk2118/pen/mdbpOWN
var options = {
chart: {
height: 310,
type: 'line',
stacked: false,
},
stroke: {
width: [0, 2, 5],
curve: 'smooth'
},
plotOptions: {
bar: {
columnWidth: '50%'
}
},
series: [{
name: 'Series Column',
type: 'column',
data: [23, 11, 22, 27, 13, 22, 37, 21, 44, 22, 30]
}, {
name: 'Series Area',
type: 'area',
data: [44, 55, 41, 67, 22, 43, 21, 41]
}, {
name: 'Series Line',
type: 'line',
data: [30, 25, 36, 30, 45, 35, 64, 52, 59, 36, 39]
}],
fill: {
opacity: [0.85,0.25,1],
gradient: {
inverseColors: false,
shade: 'light',
type: "vertical",
opacityFrom: 0.85,
opacityTo: 0.55,
stops: [0, 100, 100, 100]
}
},
labels: ['01/01/2003', '02/01/2003','03/01/2003','04/01/2003','05/01/2003','06/01/2003','07/01/2003','08/01/2003','09/01/2003','10/01/2003','11/01/2003'],
markers: {
size: 0
},
xaxis: {
type:'datetime'
},
yaxis: {
title: {
text: 'Points',
},
min: 0
},
legend: {
show: false
},
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function (y) {
if(typeof y !== "undefined") {
return y.toFixed(0) + " points";
}
return y;
}
}
}
}
var chart = new ApexCharts(
document.querySelector("#chart"),
options
);
chart.render();
// check if the checkbox has any unchecked item
checkLegends()
function checkLegends() {
var allLegends = document.querySelectorAll(".legend input[type='checkbox']")
for(var i = 0; i < allLegends.length; i++) {
if(!allLegends[i].checked) {
chart.toggleSeries(allLegends[i].value)
}
}
}
// toggleSeries accepts a single argument which should match the series name you're trying to toggle
function toggleSeries(checkbox) {
chart.toggleSeries(checkbox.value)
}
Your codepen example uses an older version of ApexCharts.
Please update by pulling the latest version here
https://cdn.jsdelivr.net/npm/apexcharts#latest
Also, if you have a different number of data-points in each series, you can turn off shared tooltip and only show the tooltip when the user hovers over bars/markers directly.
tooltip: {
shared: false,
intersect: true
}
I'm doing an app that displays daily personal work data.
I store the data in an array called "dailyTimePerProjectPerWeek", for now I define the data for 3 weeks.
I have placed two buttons in the html, "Previous Week" and "Next Week", to navigate between the weeks.
When the page is loaded for the first time it shows the most recent week corresponding to the highest index of the array mentioned above.
The problem is that it does something strange to me to visualize the data, it's like that I created several charts.
How can I refresh the image of the chart properly?
There goes my html code:
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Charts4DailyProgress</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<body>
<h1>Weekly Time per Project</h1>
<div id="canvas-container">
<canvas id="ctx" width="1000"></canvas>
<button type="button" onclick="decrementWeek()">Previous Week</button>
<button type="button" onclick="incrementWeek()">Next Week</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<script src="js/index.js"></script>
</body>
</html>
There goes my javascript code:
var dailyTimePerProjectPerWeek =[];
dailyTimePerProjectPerWeek[0] = {
labels: ['M', 'TU','W','TH','F','SA','SU'], // responsible for how many bars are gonna show on the chart
// create 12 datasets, since we have 12 items
// data[0] = labels[0] (data for first bar - 'Standing costs') | data[1] = labels[1] (data for second bar - 'Running costs')
// put 0, if there is no data for the particular bar
datasets: [{
label: 'Master Project. Second Part',
data: [300, 480, 360,180, 240, 300,480],
backgroundColor: '#D4AF37'
}, {
label: 'Guild Ideas - Learning Angular',
data: [60, 0, 240, 180, 120, 0, 60],
backgroundColor: '#C0C0C0'
}, {
label: 'Charts For Daily Progress',
data: [60, 180, 120, 180, 120, 120, 0],
backgroundColor: '#133a7c'
}, {
label: 'Project Manager',
data: [120, 180, 120, 120, 0],
backgroundColor: '#109618'
}, {
label: 'TOOYS',
data: [0, 180, 120, 0, 120, 0,0],
backgroundColor: '#990099'
}, {
label: 'Web Pc Explorer',
data: [0, 0, 120, 180, 0, 120, 0],
backgroundColor: '#54161F'
}, {
label: 'Mind Maps Program',
data: [0, 0, 180, 180, 0, 0, 0],
backgroundColor: '#708238'
}, {
label: 'Chain System',
data: [0, 0, 180, 0, 0, 0],
backgroundColor: '#E86100'
}, {
label: 'Code Generator',
data: [60, 0, 0, 0, 0, 0],
backgroundColor: '#F81894'
}, {
label: 'Electronic Brain',
data: [0, 0, 0, 0, 0, 0,240],
backgroundColor: '#6cc4ee'
}]
}
dailyTimePerProjectPerWeek[1] = {
labels: ['M', 'TU','W','TH','F','SA','SU'], // responsible for how many bars are gonna show on the chart
// create 12 datasets, since we have 12 items
// data[0] = labels[0] (data for first bar - 'Standing costs') | data[1] = labels[1] (data for second bar - 'Running costs')
// put 0, if there is no data for the particular bar
datasets: [{
label: 'Master Project. Second Part',
data: [0, 480, 360,180, 240, 300,480],
backgroundColor: '#D4AF37'
}, {
label: 'Guild Ideas - Learning Angular',
data: [0, 0, 240, 180, 120, 0, 60],
backgroundColor: '#C0C0C0'
}, {
label: 'Charts For Daily Progress',
data: [0, 180, 120, 180, 120, 120, 0],
backgroundColor: '#133a7c'
}, {
label: 'Project Manager',
data: [0, 180, 120, 120, 0],
backgroundColor: '#109618'
}, {
label: 'TOOYS',
data: [0, 180, 120, 0, 120, 0,0],
backgroundColor: '#990099'
}, {
label: 'Web Pc Explorer',
data: [0, 0, 120, 180, 0, 120, 0],
backgroundColor: '#54161F'
}, {
label: 'Mind Maps Program',
data: [0, 0, 180, 180, 0, 0, 0],
backgroundColor: '#708238'
}, {
label: 'Chain System',
data: [0, 0, 180, 0, 0, 0],
backgroundColor: '#E86100'
}, {
label: 'Code Generator',
data: [0, 0, 0, 0, 0, 0],
backgroundColor: '#F81894'
}, {
label: 'Electronic Brain',
data: [0, 0, 0, 0, 0, 0,240],
backgroundColor: '#6cc4ee'
}]
}
dailyTimePerProjectPerWeek[2] = {
labels: ['M', 'TU','W','TH','F','SA','SU'], // responsible for how many bars are gonna show on the chart
// create 12 datasets, since we have 12 items
// data[0] = labels[0] (data for first bar - 'Standing costs') | data[1] = labels[1] (data for second bar - 'Running costs')
// put 0, if there is no data for the particular bar
datasets: [{
label: 'Master Project. Second Part',
data: [300, 480, 360,180, 240, 300,0],
backgroundColor: '#D4AF37'
}, {
label: 'Guild Ideas - Learning Angular',
data: [60, 0, 240, 180, 120, 0, 0],
backgroundColor: '#C0C0C0'
}, {
label: 'Charts For Daily Progress',
data: [60, 180, 120, 180, 120, 120, 0],
backgroundColor: '#133a7c'
}, {
label: 'Project Manager',
data: [120, 180, 120, 120, 0],
backgroundColor: '#109618'
}, {
label: 'TOOYS',
data: [0, 180, 120, 0, 120, 0,0],
backgroundColor: '#990099'
}, {
label: 'Web Pc Explorer',
data: [0, 0, 120, 180, 0, 120, 0],
backgroundColor: '#54161F'
}, {
label: 'Mind Maps Program',
data: [0, 0, 180, 180, 0, 0, 0],
backgroundColor: '#708238'
}, {
label: 'Chain System',
data: [0, 0, 180, 0, 0, 0],
backgroundColor: '#E86100'
}, {
label: 'Code Generator',
data: [60, 0, 0, 0, 0, 0],
backgroundColor: '#F81894'
}, {
label: 'Electronic Brain',
data: [0, 0, 0, 0, 0, 0,0],
backgroundColor: '#6cc4ee'
}]
}
var currentWeek = dailyTimePerProjectPerWeek.length - 1;
var weekValue = currentWeek; //At first time weekValue points to the current week
function drawData(){
var chart = new Chart(ctx, {
type: 'bar',
data: dailyTimePerProjectPerWeek[weekValue],
options: {
responsive: false,
legend: {
position: 'right' // place legend on the right side of chart
},
scales: {
xAxes: [{
stacked: true // this should be set to make the bars stacked
}],
yAxes: [{
stacked: true // this also..
}]
}
}
});
}
function incrementWeek(){
if(weekValue === dailyTimePerProjectPerWeek.length - 1){
console.log("This is the current week");
} else {
weekValue += 1;
drawData();
}
}
function decrementWeek(){
if(weekValue === 0){
console.log("This is the oldest week of the time series");
} else {
weekValue -= 1;
drawData();
}
}
/*
function selectWeek(){
}
*/
/*
function fixWeek(){
}*/
//Main Program
var chart = new Chart(ctx, {
type: 'bar',
data: dailyTimePerProjectPerWeek[weekValue],
options: {
responsive: false,
legend: {
position: 'right' // place legend on the right side of chart
},
scales: {
xAxes: [{
stacked: true // this should be set to make the bars stacked
}],
yAxes: [{
stacked: true // this also..
}]
}
}
});
The truth is that I use the following code to try to refresh the image:
function drawData(){
var chart = new Chart(ctx, {
type: 'bar',
data: dailyTimePerProjectPerWeek[weekValue],
options: {
responsive: false,
legend: {
position: 'right' // place legend on the right side of chart
},
scales: {
xAxes: [{
stacked: true // this should be set to make the bars stacked
}],
yAxes: [{
stacked: true // this also..
}]
}
}
});
}
Change your drawData() function to:
function drawData() {
chart.data = dailyTimePerProjectPerWeek[weekValue];
chart.update();
}