I currently have a chart that shows real-time information. I tried to disable the animation for the y-axis, because the dots hopped around along the y-axis which creates a weird effect. But I still want that new dots fade in smoothly along the x-axis.
I tried it with this configuration:
const chartOptions: ChartOptions = {
animations: {
x: {
duration: 500
},
y: false,
},
// ...
};
The result is no animation at all. Not on the y-axis, but also not on the x-axis. It doesn't look smooth anymore.
And after 25 data points I shift()/push(newDataPoint) in a separate array and then replace the whole data array for the chart as I use ChartJS with the vue-chartjs library.
I need the exact same behavior like in the GIF above, except that it should not stutter but scrolling smooth along the x-axis.
Whole vue-chartjs example for reference
<script setup lang="ts">
const chartData = ref<ChartData<'line'>>({
labels: [],
datasets: []
})
const chartLabels: string[] = Array(maxDataPoints).fill('');
const chartDataPoints: number[] = Array(maxDataPoints).fill(18.3);
function fillData() {
if (chartDataPoints.length > maxDataPoints) {
chartLabels.shift();
chartDataPoints.shift();
}
chartLabels.push(new Date().toLocaleString())
chartDataPoints.push(Number((currentDistance.value * 0.1).toFixed(1)))
const updatedChartData: ChartData<'line'> = {
labels: [...chartLabels],
datasets: [
{
label: 'Distance',
tension: 0.5,
data: [...chartDataPoints],
}
]
}
chartData.value = { ...updatedChartData }
}
onMounted(() => {
fillData();
setInterval(() => fillData(), 500)
})
const chartOptions: ChartOptions = {
responsive: true,
maintainAspectRatio: false,
//animation: false,
animations: {
x: {
duration: 500
},
y: false,
},
scales:{
x: {
display: false,
},
y: {
suggestedMin: 0,
suggestedMax: 20,
}
},
plugins: {
legend: {
display: false
},
},
}
</script>
<template>
<LineChart :chartData="chartData" :chartOptions="chartOptions" />
</template>
In the end I used the chartjs-streaming-plugin by nagix which does exactly what I was looking for.
Related
I am trying to follow the vue documentation on ApexCharts website.
https://apexcharts.com/vue-chart-demos/line-charts/realtime/
and the way they structured the component isn't practical. and seem like they have some missing methods in the example.
I wish to create a chart as in the example above that fetches data every second (1 second worth data)
<template>
<VueApexCharts
type="line"
height="350"
width="100%"
:options="chartOptions"
:series="series"
ref="chart"
/>
</template>
<script>
import { ref } from "vue";
import VueApexCharts from "vue3-apexcharts";
import { useStore } from "vuex";
export default {
name: "ChartApex",
components: { VueApexCharts },
props: ["session"],
setup(/* props */) {
let store = useStore();
let chartStream = undefined;
let series = [
{
name: "data1",
data: ref([]),
},
{
name: "data2",
data: [],
},
];
let chartOptions = {
chart: {
// id: "realtime",
width: "100%",
height: 350,
type: "line",
animations: {
enabled: true,
easing: "linear",
dynamicAnimation: {
speed: 1000,
},
},
toolbar: {
show: true,
},
zoom: {
enabled: true,
},
},
dataLabels: {
enabled: true,
},
stroke: {
curve: "straight",
},
title: {
text: "Chart",
align: "left",
},
grid: {
row: {
colors: ["#f3f3f3", "transparent"],
opacity: 0.5,
},
},
xaxis: {
type: "numeric",
show: true,
range: 3,
},
yaxis: {
min: -32768,
show: true,
max: 32768,
},
};
return {
chartOptions,
series,
store,
chartStream,
};
},
watch: {
"session.device.isDataStream"(newVal) {
if (newVal) this.startStream();
else this.endStream();
},
},
methods: {
startStream() {
console.log("starting chart stream");
this.chartStream = setInterval(() => {
// Push data
this.series[0].data.value.push(
...this.store.state.session.samples[0].at(-1)
);
}, 1000);
},
endStream() {
console.log("end chart srtrem");
clearInterval(this.chartStream);
},
},
};
</script>
I wish to display a range of 10 seconds on the X-axis (as in the example above).
When getting a new 1 second worth data: i would like my chart to show extra data and move one second ahead (x-axis) so it always remain as 10 seconds in total.
I am fetching data from my store which keeps updating in chunks. unfortunatly it stacks all in one chart that doesn't move. I wish to display one chunk every second.
I would prefer linking my data directly to the store variable. I am not doing this because i would need to use computed method, and i think it's less efficient (is it?)
the store.state.samples[0] is an array of chunks with around 1 second worth of data in each, structured as apex-charts use: [ts, sample] - looks like this:
// samples[0]
[
// Chunk 1 of almost a second
// [0] =>
[
[ // sample
8.75,
0
],
[ // sample
17.5,
-1
],
[ // etc
26.25,
-2
],
[
35,
-3
],...
],
[ // Chunk 2 of almost a second.
[
958.6875,
-12747
],
[
962.375,
-12808
],
[
966.0625,
-12789
],...
]
]
so that basically i push a chunk worth a second to my chart (..every second)
A lot recommend on apex-charts as a go-to charts library for vue, but seems they lack of proper documentation.
-
Here is link to the source code of the sample. Check there how it was built.
To show the range of 10 seconds use the xaxis.range setting:
const TICKINTERVAL = 1000;
const XAXISRANGE = 10000;
const ARRAYLENGTH = XAXISRANGE / TICKINTERVAL + 1;
xaxis: {
type: 'datetime',
range: XAXISRANGE,
position: 'bottom',
To move the X-Axis you should slice you data array, like the resetData() function does:
function resetData(){
data = data.slice(data.length - 10, data.length);
}
I did it with my ARRAYLENGTH constant
data.push(...new Data...);
if (data.length > ARRAYLENGTH) data = data.slice(-ARRAYLENGTH);
I am trying to make a chart which has years along the x-axis and dollar amounts along the y-axis. I finally got close to what I'm looking for, but I found that because the x coordinates are numbers, ChartJS is putting commas in them which looks really strange for years.
After some digging, I used the callbacks. options.plugin.tooltip.callbacks.label worked to let me remove commas in the tooltips, but when I use options.scales.x[0].ticks.callback to try to fix the labels on the bottom, not only does it not work, but I don't see the console.log statement in their ever being printed so it seems it's not even calling the callback. I've tried several variations of how to do the callback based on what I found online and on Stack Overflow which I think correspond to the different ways ChartJS did this in different versions. (I'm on version 3.5.1.)
Then, I realized that... none of the options under options.scales appear to have any effect. I change the min, the title, the tick settings (color to red, callback, etc.) and it has no effect. (This also explains why I was having trouble when using the line chart and had to switch to scatter; apparently type: 'linear' wasn't being picked up nor did it do anything different when I set it to type: 'date' or whatever the exact working was for that.)
Meanwhile, the other options like options.showLine or options.elements do have an effect and I'm seeing the chart and not getting any errors in the console. So, it is picking up the options, just ignoring everything I have in options.scales.
Here is the relevant code:
// Sample data added to make this example self-contained
// This is my internal data format
let data = {
"Series1": [ {x: 2001, y: 100 }, {x: 2002, y: 110 }, {x: 2003, y: 107 }, ],
"Series2": [ {x: 2001, y: 107 }, {x: 2002, y: 102 }, {x: 2004, y: 95 }, ],
}
// Define data //////////////////////////////////////////////////////
// I convert data to format ChartJS wants and add a few options
let datasets = [];
for(let label in data) {
let c = colorIterator.next().value
datasets.push({
label: label,
data: data[label],
backgroundColor: c,
borderColor: c,
});
}
// Define options //////////////////////////////////////////////////////
let chartConfig = {
type: 'scatter',
data: { datasets: datasets, },
options: {
title: { display: false },
indexAxis: 'x', responsive: true, maintainAspectRatio: false,
showLine: true,
elements: {
line: { display: true, tension: 0, borderWidth: 1, fill: false, },
point: { radius: 3 }
},
interaction: { mode: 'x', },
scales: {
x: [{
type: 'linear',
min: 1995, max: (new Date()).getFullYear()+1, stepSize: 1,
title: { display: true, text: 'Year' },
ticks: {
display: true,
major: { enabled: true },
color: 'red',
callback: function(value, index, ticks) {
console.log(value);
return Chart.Ticks.formatters.numeric.apply(this, [value, index, ticks])
.replace(",","");
}
}
}],
y: [{
title: { display: true, text: '$' },
ticks: {
display: true,
color: 'red',
},
}],
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if(label) {
let x = context.label.replace(",","");
let y = context.formattedValue;
return 'Year ' + x + ' "' + label + '": $' + y;
} else { return 'x'; }
},
},
},
},
}
};
// MAKE CHART //////////////////////////////////////////////////////
let mainChart = new Chart(document.getElementById(c.id), chartConfig);
As described in the docs the scales are not arrays. All the scales are objects in the scale object.
So you will need to change your code to this:
options: {
scales: {
x: {
// x options
},
y: {
// y options
},
}
}
So I've got a pretty small div, in which I gotta get a small timeline chart shoved in. The current problem is, that due to the chart being perfectly centered, I have a lot of whitespace I could really use, to make the actual data and chart much larger.:
I think showing the parent div shouldn't matter(because changing around these sizes doesn't really affect the chart itself). It feels like there's some option in the configuration that I'm missing. I've tried setting offsetY and even offsetX to negative values, because I was desperate, but to no avail.
<html>
<div id="chartTarget" style="height: 100; width: 100%;">
<canvas id="chartCanvas" height="100"></canvas>
<div style="opacity:0;" class="chartTooltip center">
</div>
</div>
</html>
Relevant code is pretty much straight up copied from the example, but since I'm bound to get questions about it, here goes:
var options = {
series: [
{
name: 'Pending',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T03:24:00').getTime(),
new Date('2021-04-21T04:24:00').getTime()
]
}
]
},
{
name: 'Active',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T05:24:00').getTime(),
new Date('2021-04-21T08:24:00').getTime()
]
}
]
},
{
name: 'Deleted',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T11:24:00').getTime(),
new Date('2021-04-21T12:24:00').getTime()
]
}
]
}
],
chart: {
height: 75,
width: 250,
type: 'rangeBar',
offsetY: -25,
toolbar: {
show: false,
tools: {
download: false,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: true,
reset: true
}
},
events: {
dataPointMouseEnter: function (event, chartContext, config) {
config.w.config.chart.toolbar.show = false;
},
dataPointMouseLeave: function (event, chartContext, config) {
config.w.config.chart.toolbar.show = false;
}
}
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '50%',
rangeBarGroupRows: true
}
},
colors: [
"#008FFB", "#00E396", "#FEB019", "#FF4560", "#775DD0",
"#3F51B5", "#546E7A", "#D4526E", "#8D5B4C", "#F86624",
"#D7263D", "#1B998B", "#2E294E", "#F46036", "#E2C044"
],
fill: {
type: 'solid'
},
xaxis: {
type: 'datetime',
labels: {
maxHeight: 25,
style: {
fontSize: '9px'
},
offsetY: -8
},
tooltip: {
enabled: false
}
},
yaxis: {
show: false,
lines: {
show: true
}
},
legend: {
show: false,
position: 'top'
}
};
var chart = new ApexCharts(document.querySelector("#chartTarget"), options);
chart.render();
I need the label, so things like sparkline aren't useful.
A negative offset at the chart level seems to have fixed for my needs.
chart.offsetY: -25
I assume everyone else can play with negative offset values, where available for more fine tuning.
I am creating a cryptocurrency price tracker with React. I am using axios to make API requests and get information about the price history of different cryptocurrencies. This works fine, but when I am creating the chart with ChartJS, the chart only shows one value, even though I have thousands. Anyone know how to fix this? Big thanks in advance.
Here is my code for the chart component:
import Chartjs from "chart.js";
import axios from "axios";
import { historyOptions } from "../../chartConfigs/chartConfigs";
const HistoryChart = ({ coinId }) => {
const [timeLength, setTimeLength] = useState(30);
const chartRef = useRef();
const [timeSeriesData, setTimeSeriesData] = useState([]);
useEffect(() => {
const timeSeriesDataArray = [];
axios.get(`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${30}`)
.then(response => {
response.data.prices.forEach(element => {
timeSeriesDataArray.push(
{
t: element[0],
y: element[2].toFixed(2)
}
)
})
setTimeSeriesData(timeSeriesDataArray)
})
.catch(error => console.log(error))
}, [coinId, timeLength])
useEffect(() => {
if (chartRef && chartRef.current) {
const chartInstance = new Chartjs(chartRef.current,{
type: 'line',
data: {
datasets: [{
label: "Price",
data: timeSeriesData,
backgroundColor: "rgba(174, 374, 194, 0.5)",
borderColor: "rgba(174, 374, 194, 0.4)",
pointRadius: 0,
borderWidth: 1
}]
},
options: historyOptions
})
}
}, [timeSeriesData])
return (
<div className="history-chart-container">
<canvas ref={chartRef} id="history-chart" width={1200} height={500}></canvas>
</div>
)
}
export default HistoryChart
Here is my code for the chart options (historyOptions):
lineHeighAnnotation: {
always: true,
hover: false,
lineHeight: 1.5
},
animation: {
duration: 2000
},
maintainAspectRatio: false,
responsive: true,
scales: {
xAxes: {
type: "time",
distribution: "linear",
}
}
}```
[1]: https://i.stack.imgur.com/e7g4P.png
[2]: https://i.stack.imgur.com/t4YIa.png
This could be fixed by changing your line chart into a scatter chart.
const chartInstance = new Chartjs(chartRef.current,{
type: 'scatter',
...
To still get the lines drawn between the data points, you need to define the option showLine: true on the dataset.
datasets: [{
label: "Price",
showLine: true,
...
}]
UPDATE
You should also make sure to update the chart when new data is added and the chart already exists. Not knowing much about react.js, you could make chartInstance a global variable and proceed as follows:
if (chartRef && chartRef.current) {
chartInstance = new Chartjs(chartRef.current,{
type: 'line',
...
} else {
chartInstance.update();
}
Hi I'm new at charts and this is my chartjs chart, it's working currently, but it's showing in sharp lines and I want to make it smooth curve lines on this chart. Any ideas?
function statistics(data) {
if ($('#stats-currency').length > 0) {
if (typeof(stats_currency) !== 'undefined') {
stats_currency.destroy();
}
if (typeof(data) == 'undefined') {
var currency = $('select[name="currency"]').val();
$.get(admin_url + 'home/stats_currency/' + currency, function(response) {
stats_currency = new Chart($('#stats-currency'), {
type: 'line',
data: response,
options: {
responsive:true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
},
});
}, 'json');
} else {
stats_currency = new Chart($('#stats-currency'), {
type: 'line',
data: data,
options: {
responsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
},
});
}
This can be done through the option lineTension that needs to be defined on your dataset. Choose a value below 1.
datasets: [{
...
lineTension: 0.8
}]
By default, you should however already see curved smooth lines since accoring to Chart.js documentation, the default value is 0.4.
lineTension: Bezier curve tension of the line. Set to 0 to draw straight lines.
Please note that if the steppedLine value is set to anything other than false, lineTension will be ignored.
you can do it by adding tension value to your charts options
<canvas id="myChart"></canvas>
JS
const config = {
type: 'line', // your chart type
data: data, // pass here your data
options: {
elements: {
line: {
tension : 0.4 // smooth lines
},
},
},
};
// pass it like
const myChart = new Chart(
document.getElementById('myChart'),
config
);