Apexcharts, how do I find positional value for the highest and lowest marker on the line, so i can add an image to that one specific marker? - apexcharts

I would like to add arrows to the chart, I am guessing if i can find the position of the highest marker, I can use it to add some html to get some custom tool-tips or markers.
I tried looking for a format function in markers, and in the axis, I just am not sure what I am looking for here.
Any help is much appreciated. I haven't been able to wrap my head around this one.

The feature is a WIP and I am in the process of creating examples to illustrate the functionality.
The proposed API for the feature will look like below
var options = {
chart: {
height: 350,
type: "area",
events: {
mounted: function(ctx, config) {
const lowest = ctx.getLowestValueInSeries(0)
const highest = ctx.getHighestValueInSeries(0)
ctx.addPointAnnotation({
x: new Date(ctx.w.globals.seriesX[0][ctx.w.globals.series[0].indexOf(lowest)]).getTime(),
y: lowest,
label: {
text: 'Lowest: ' + lowest,
offsetY: 2
},
customSVG: {
SVG: `<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
<path d="M0 0h24v24H0z" fill="none"/>`,
cssClass: undefined,
offsetX: -10,
offsetY: -30
}
})
ctx.addPointAnnotation({
x: new Date(ctx.w.globals.seriesX[0][ctx.w.globals.series[0].indexOf(highest)]).getTime(),
y: highest,
label: {
text: 'Highest: ' + highest,
offsetY: 2
},
})
}
}
},
dataLabels: {
enabled: false
}
}
which will produce the following result
The functionality is not available in the current release (3.5.1) of ApexCharts, but it will soon be added in 3.6.0 which is going to be released this weekend (March 10, 2019).
Disclaimer: I am the creator of ApexCharts

Related

ApexCharts skipping X axis ticks

I would like to present a bar chart using Apex Charts.
The graphs shows some items, the more items the longer you can do some stuff. The items are presented in the Y axis. The X axis should represent months in a year, therefore I want it to be fixed between 0 and 12.
With the following options, it skips some numbers in the x axis:
var options = {
series: [{ data: [3,4,6,7,8.5] }],
chart: { type: 'bar', height: 250 },
plotOptions: {
bar: { borderRadius: 4, horizontal: true }
},
dataLabels: { enabled: false },
xaxis: {
categories: [2,3,5,6,7],
min: 0,
max: 12,
},
yaxis: {
labels: { formatter: function (val) { return val + ' items'; } },
},
title: {
text: 'Chart title', floating: true, align: 'center',
style: { color: '#444' }
}
};
If I omit the min/max in the xaxis, it works fine but the 12 months are not fixed as required.
In the example the 9 is skipped, but the actual bars are scalled correctly
I am loading the library using de direct script include:
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
Here you can find the official example/docs: https://apexcharts.com/javascript-chart-demos/bar-charts/basic/
Have I overlooked something? Or is it a bug?
Setting tickAmount to 12 seems to fix the problem, but I does not behave correctly with other numbers. (if I use 13 then it inserts two 6 tiks instead of inserting decimals)

Chart.JS 2 automatic horizontal scroll upon changes on xAxis range not working anymore on Chart.JS 3?

I previously had a scatter graph in Chart.js 2 with the zoom plugin, in which I just had to add data and change the xAxis ticks' min/max to see a nice animated horizontal scroll from my old xAxis range to my new xAxis range.
For some reason I now use Chart.js 3.24 and the zoom plugin 1.1.1. Now the same graph with nearly the same options became quite ugly when animated:
newly added points e.g. at position (X,Y) have an animation going from (X,0) to (X,Y), instead of appearing directly at (X,Y).
when a point from a dataset is at the same position than a line from another dataset, they move at a different speed.
if many points and line are added, sometimes the lines are only shown after the "scrolling animation" ended.
sometimes a point appear at its correct final position before the "scrolling animation" even started.
The only solution I found was to disable animation when updating my graph, then use window.requestAnimationFrame to manually pan the graph myself with the zoom plugin.
Are you aware of a better/simpler recommended way to achieve this ?
Thanks in advance,
My old Chart.js options:
let zoom_options = {
pan: {
enabled: true,
mode: 'x',
rangeMin: { x: 0, y: null},
rangeMax: { x: null, y: null}
},
zoom: {
enabled: true,
drag: false,
mode: 'x',
rangeMin: { x: 0, y: null },
rangeMax: { x: null, y: null },
speed: 0.1
}
};
var ctx = graph.getContext('2d');
this._chartjs = new Chart(ctx, {
type: 'scatter',
data: {
datasets: []
},
options: {
legend: {
//display: false
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
}]
},
plugins: {
zoom: zoom_options // https://github.com/chartjs/chartjs-plugin-zoom
}
}
});
My old line options :
{
label: name,
showLine: false,
fill:false,
spanGaps: false, // I have some NaN values in my datasets.
backgroundColor: color.points_borders,
borderColor: color.line,
borderWidth:5,
pointRadius:5,
pointBorderWidth:2,
pointBorderColor: color.points_borders,
pointBackgroundColor: color.points,
data: []
}
My old update function:
this._chartjs.options.scales.xAxes[0].ticks.min = min;
this._chartjs.options.scales.xAxes[0].ticks.max = length;
this._chartjs.update();

How do you get the width of a datalabel from the Chartjs plugin after the chart animates?

Here is a codepen that I am using to solve this problem. What I would like to do is get the length of the horizontal bars to determine if the label should be plotted inside or outside of the bar. Currently, what I have happening:
{
datalabels: {
color: function(context) {
return [0, 3].includes(context.dataIndex) ? 'black' : 'white'
},
anchor: 'start',
align: 'right',
offset: function(context) {
const chart = context.chart;
const area = chart.chartArea;
const meta = chart.getDatasetMeta(context.datasetIndex);
const model = meta.data[context.dataIndex];
// model.width is NaN
// is there a way to get this value
// after the animation is complete?
console.log(model, model.width)
return 4;
},
font: {
size: 9
}
}
When you run the codepen you notice that model.width prints as NaN but when you look at the object itself model.width is there. If I introduce a setTimeout to log that value it exists (not NaN). When I turn the animation off model.width is available in the function.
Therefore, I think the way to make this happen is to get the values after the animation renders. Is there a way to do that in the offset function for datalabels or is there another way of doing that?
You can use the getProps on the model to get the width after the animations are over like so:
offset: function(context) {
const chart = context.chart;
const area = chart.chartArea;
const meta = chart.getDatasetMeta(context.datasetIndex);
const model = meta.data[context.dataIndex];
const {
width
} = model.getProps(['width'], true);
console.log(width)
return 4;
},
Updated codepen: https://codepen.io/leelenaleee/pen/MWQGbdM?editors=1010
I might have been thinking about it the wrong way. By playing around with the values I realized the value itself is a pretty good indication of whether it should be inside or outside of the bar. What I've done instead is evaluate if the value is greater than 30. If so the color is white and the anchor is set to start. If it less than 30 the color is black and the anchor is set to end:
https://codepen.io/thoughtassassin/pen/rNJvOrj
plugins: {
datalabels: {
color: (context) => getValue(context) > 30 ? '#fff' : '#000',
anchor: (context) => getValue(context) > 30 ? 'start' : 'end',
align: 'right',
offset: 5,
font: {
size: 9
}
},
}

Change color of a single point by clicking on it - Chart.JS

I'm building a scatter chart using Chart.JS(latest version), one of the behaviours I'm trying to implement is clicking on a single point and highlighting it by changing the background color of the selected point.
I've used the getElementsAtEvent method from the Chart.JS API in order to get the active element and change it's background. For a brief moment I can see it changing the color but it returns to its original color and all the other points now have the color I wanted to apply to the selected one... I tried various approaches to this, using the updated and render methods but with no desired result...
Here's the code inside the function that'll run onClick
function (evt, activeElements, chart) {
const selectedPoint = chart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
selectedPoint[0].element.options.backgroundColor = '#fa6400';
chart.update();
}
Here's a fiddle
https://jsfiddle.net/dc3x70yg/1/
Thanks in advance
You can define the option pointBackgroundColor on the dataset. When the user clicks on a point, you recreate pointBackgroundColor, but now use an array that contains the desired color for each point.
Please take a look at your amended code below and see how it works.
new Chart('myChart', {
type: 'scatter',
data: {
datasets: [{
label: '# of Votes',
data: [{ x: -10, y: 0 }, { x: 0, y: 10 }, { x: 10, y: 5 }, { x: 0.5, y: 5.5 }],
pointBackgroundColor: '#ddd',
pointRadius: 5,
}]
},
options: {
onClick: (event, elements, chart) => {
const dataset = chart.data.datasets[0];
dataset.pointBackgroundColor = dataset.data.map((v, i) => i == elements[0]?.index ? '#fa6400': '#ddd');
chart.update();
},
scales: {
y: {
beginAtZero: true
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
<canvas id="myChart" width="400" height="200"></canvas>

How to limit chart JS hover to take only one value from each line chart when zoomed out?

I'm using chart.js 3.6.1 with VueJS. These are options for particular data I'm having trouble:
hover: {
mode: 'x',
intersect: false
},
scales: {
y: {
// not relevant
},
x: {
type: 'time',
min: minRangeDate.value,
max: maxRangeDate.value,
time: {
displayFormats: {
day: 'dd.MM.',
hour: 'HH:mm',
minute: 'HH:mm',
second: 'HH:mm:ss'
}
},
grid: {
display: true
},
ticks: {
color: 'white'
}
}
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x',
modifierKey: 'ctrl'
},
zoom: {
mode: 'x',
wheel: {
enabled: true,
speed: 0.2
},
pinch: {
enabled: true
}
},
limits: {
x: {
min: minRangeDate.value,
max: maxRangeDate.value,
minRange: 20000 // minimum zoom to seconds
}
}
}
}
}
When the graph is zoomed everything looks fine like in the picture below:
but when the graph is zoomed out hover is picking multiple points which is not desired behavior, pic below:
And as far as zoom is, the more points hover is picking.
Any idea how to overcome this. I've tried using 'point' and 'nearest' but then I get only one data in the tooltip as it is the default behavior.
Also, using 'index' is not possible because two datasets are not always the same length and it is not logical and desired to compare values by index.
Ok, here is the solution I came up with, followed by this ChartJS issue on GitHub. I've made a filter for tooltip and it looks like this now:
plugins: {
tooltip: {
mode: 'x',
displayColors: false,
intersect: false,
filter: function (tooltipItem, currentIndex, tooltipItems) {
// On zoomed out graph, hovering mouse picks a lot of data for x-axis and displays them all in tooltip.
// This filter returns only first item from each dataSet from hover picked items passed
// to tooltip, so it can be correctly displayed in tooltip for different line charts(datasets)
return tooltipItems[currentIndex].datasetIndex !== tooltipItems[currentIndex - 1]?.datasetIndex
},
.
.
.
}
Hover still selects multiple data, but the tooltip shows only one for each dataset.