I was not able to find an answer to the following what I think is best described by example:
I change one single value in radar-chart-data from 6 to 10 while the other ones stay the same. If I now trigger an update with non-zero animation-time it will repaint the chart while starting from zero for each value. I would prefer the animation to just animate the changing, i.e. a movement from 6 to 10 for the property in question. Is that possible?
Below is an example showing the chart updating without redrawing. Run the snippet and press the 'Update' button to increase data point b by 1 each time.
let myChart = new Chart(document.getElementById('chart'), {
type: 'radar',
data: {
labels: ['a', 'b', 'c', 'd', 'e'],
datasets: [{
label: 'series1',
data: [0, 2, 7, 10, 3]
}]
},
options: {
maintainAspectRatio: false
}
});
document.getElementById('update').addEventListener('click', function() {
myChart.data.datasets[0].data[1] += 1;
myChart.update();
});
<button id="update">Update!</button>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="chart"></canvas>
Yes. It is possible. It depends on how you update the chart option. See chartjs documentations
To update the options, mutating the options property in place or passing in a new options object are supported.
If the options are mutated in place, other option properties would be preserved, including those calculated by Chart.js.
If created as a new object, it would be like creating a new chart with the options - old options would be discarded.
Related
Let's take a look at the simple chart data:
it has (x,y) pairs:
x
y
0
-3
1
2
2
7
3
8
4
15
5
0
The idea is to create a basic line chart, using VueJS in my case, but the idea can be generalized to JavaScript.
I have a series array of objects, where each object has x and y coordinates:
series = [
{
x: 0,
y: -3
},
{
x: 1,
y: 2
},
...
]
This series is part of options object:
const options = {
chart: {
type: 'line'
},
series: series
}
const chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
And the chart is rendered.
Now, let's say I want to append the data to that chart - add 2 new (x,y) pairs
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendData([{ data: chartData }])
I would also like that newly appendedData has, for example, different color, fill, or something else - so newly added data displays differently than old data.
Feel free to point me to documentation if I missed anything, but I searched through apex chart methods, and the only thing that looks remotely close to this would be inside
updateOptions() method, the redrawPath flag (updateOptions docs:
When the chart is re-rendered, should it draw from the existing paths
or completely redraw the chart paths from the beginning. By default,
the chart is re-rendered from the existing paths
In order to style the new data differently, you'll want to put these data points into a different series using the appendSeries method:
const newData = [
{
x: 6,
y: 20
},
{
x: 7,
y: -10
}
]
chart.appendSeries({
name: "series-2", // optional
data: newData
})
Most of the styling in ApexCharts is done based on series (more specifically seriesIndex). So by placing the new data in a separate series you'll be able to style this second series using an array of, for example, colors.
You could either specify the color you would like to use as you append the new series of data using the updateOptions method you mention, or you can specify it in advance.
chartOptions: {
colors: ["#546E7A", "#E91E63"],
}
When working with "Numeric paired values in XY properties", the xaxis type also has to be explicitly set to numeric:
chartOptions: {
xaxis: {
type: 'numeric',
},
}
The tricky bit comes when you want to add more data a second time (or third, more time). There are two approaches I can think of here:
Shuffle the existing data across to the original series (append series-2 to series-1) - and overwrite series-2 with your new data. You don't need to edit the colors in chartOptions.
You could shuffle the colors along. If you want all "old" data to have the same color, simply prepend the colors array with your base color every time you add a new series. Or if you want each series to have a different color, just append a color every time you add a new series.
I am using latest Chart.bundle.js to create multiple stacked Bar charts on same page. For example, I have 3 charts with 2 datasets. Charts are correct, but tooltips on each of the charts are always the same and showing wrong values. screenshot link. All the values are zero, which is obviously wrong.
Tooltips mode is index, canvas for each chart has different id, dataset variables are all different.
Trying to post my code on jsfiddle, I was able to find the solution to this very odd problem. I had
var barCharData = {
labels: ["2", "3", "4"],
datasets: []
};
, so there is no data in any of my charts at the beginning (before getting data from database). For every chart I have I did something like this:
for(var i=1; i <= number_of_charts; i++){
bar_char_data_array.push(barChartData);
var myBar = new Chart(ctx, {
type: 'bar',
data: bar_char_data_array[i-1],
options: {
tooltips: {
mode: 'label',
intersect: true
}
}
});
}
It turned out that javascript considers every element of array "bar_char_data_array" to be the same object and changing data for one of the charts resulted in changing data for all the rest. I still don't know why all the charts were correct until hovered to see tooltip. Nevertheless, solution for my problem was to remove barChartData variable.
for(var i=1; i <= number_of_charts; i++){
bar_char_data_array.push({
labels: ["2", "3", "4"],
datasets: []
});
var myBar = new Chart(ctx, {
type: 'bar',
data: bar_char_data_array[i-1],
options: {
tooltips: {
mode: 'label',
intersect: true
}
}
});
}
My solution was somewhat similar to the #queen-juliet's answer.
I had a shared config variable with the data key being added and updated within a loop. Even though the multiple chart.js objects I had on the page were showing the correct data, the tooltips were shown on one (random) chart only. The solution was to declare and fill the whole config inside my loop instead.
I need to stack the bars in the bar chart to the left as per the image attached
is there a way to do that in chart.js?
EDIT:
Just to clarify what I am looking for.
The number of the bars in my chart is dynamic, if there are 10 of them then chart looks fine but if there are only 2 they each take 50% of the width of the chart (see picture #2)
I want both of those bars to be exactly the same width as if there were 10 of them and be stacked to the left.
One option that I'm currently considering is just to add (10 - no of bars) bars with 0 value so that they won't be visible. But I'm hoping that there is a better solution.
Thanks.
Instead of creating a graph with 10 empty bar charts, then populate it with your values, I think it would be better to add empty values to reach the number of 10 (same idea though).
If you take a look in the Chart.js documentation, you can see that you can create plugins for your charts and graphs. Plugins are extremely useful when editing your chart (instead of just hardcoding what you want) since they allow you to handle what is happening while creating your charts.
For instance : beforeUpdate, afterDraw are some of the events you can handle with plugins.
Now, you must know that the chart object contains a lot of information :
If you want to edit a global option, you'd check chart.config.options
If you want to edit a specific chart, you'd check chart.config.data
In our case, we'd need the data attribute.
If you take a deep look in it, you'd see that the number of values come from the lengh of both data.labels and data.datasets[n].data (n being the nth dataset).
Now that you know what to do, and how to do it, you can do it.
I still made a quick example of what you are looking for :
var ctx = document.getElementById("myChart").getContext("2d");
// stores the number of bars you have at the beginning.
var length = -1;
// creates a new plugin
Chart.pluginService.register({
// before the update ..
beforeUpdate: function(chart) {
var data = chart.config.data;
for (var i = data.labels.length; i < data.maxBarNumber; i++) {
length = (length == -1) ? i : length;
// populates both arrays with default values, you can put anything though
data.labels[i] = i;
data.datasets[0].data[i] = 0;
}
},
// after the update ..
afterUpdate: function(chart) {
console.log(chart);
var data = chart.config.data;
if (length == -1) return;
// prevents new charts to be drawn
for (var i = length; i < data.maxBarNumber; i++) {
data.datasets[0]._meta[0].data[i].draw = function() {
return
};
}
}
});
var data = {
// change here depending on how many bar charts you can have
maxBarNumber: 10,
labels: [1, 2, 3, 4, 5, 6, 7],
datasets: [{
label: "dataset",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
data: [65, 59, 80, 81, 56, 55, 40],
}]
};
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [{
display: false
}],
yAxes: [{
stacked: true
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.1/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
You can use Array.prototype.reverse() to reverse the data if it is currently stacked to the right. Otherwise you will need to use some type of sorting to go from largest data to smallest data.
Edit: Here is a Fiddle - slightly different (simplified) code to examples below but same problem.
I have a simple Highcharts bar chart showing two series of data for 2011 (truncated):
var dataset = {};
dataset.attchange2011 = [
{y: -8.5},
{y: -8.3}
];
dataset.revchange2011 = [
{y: -14.9},
{y: -10.7}
];
This is displaying properly in the graph on load:
...
series: [{
name: 'Change in Revenue',
data: dataset['revchange2012']
},{
name: 'Change in Attendance',
data: dataset['attchange2012']
}]
...
I have a second set of data for 2012:
dataset.attchange2012 = [
{y: 1.2 },
{y: 14.1}
];
dataset.revchange2012 = [
{y: 11.5},
{y: 37.5}
];
And a simple function to switch between the years by getting the data-year value from a link that's clicked, loop through that year's data and update the series points values, followed by a redraw:
...
year = $(this).data('year').toString();
$.each(chart.series[0].data, function (i, point) {
point.update(dataset['revchange'+year][i], false);
});
$.each(chart.series[1].data, function (i, point) {
point.update(dataset['attchange'+year][i], false);
});
...
The first time the 2011 link is clicked, the data updates correctly. Trying to switch back to 2012 doesn't work.
When looking at each data set at various stages by console.log(dataset), it appears that it is correctly set on page load:
dataset
Object
attchange2011: Array[8]
0: Object
y: -8.5
...
attchange2012: Array[8]
0: Object
y: 1.2
But changes when the link is clicked - 2011 values are copied to the 2012 data set:
dataset
Object
attchange2011: Array[8]
0: Object
y: -8.5
...
attchange2012: Array[8]
0: Object
y: -8.5
I can't figure out where or why it would be doing that. Any ideas? I am not completely against rewriting everything from scratch if needed.
It's caused by Highcharts. Variables are overwritten when updating points. Instead, use copy of that objects: http://jsfiddle.net/RF7aW/7/
series: [$.extend(true, {}, data2013[0]), $.extend(true, {}, data2013[1])],
If not against rewriting, I suggest a much cleaner approach which would be this:
Define an actual function, function makeChart(data) and initialize it like you do now, giving it the 2012 data. On click of 2012 or 2011 or whatever other button, simply call makeChart(otherData). This extracts the whole updating issue which, by the way, you don't need to iterate through every single point, you can just update the data series entirely with series.update (which I believe redraws anyways).
EDIT:
If not redrawing, try using simple buttons and using series.setData() instead:
JSfiddle
I'm being able to use JQuery Flot, and it's a very nice tool. However, I could not find a GOOD solution for my problem.
I want to duplicate Y axis, so I can display 1 on the left and 1 on the right, so the users, when comparing data from the rightmost side of the chart, won't have to scroll through the leftmost side of the chart. I'm assuming they will be accessing it through a smartphone.
JQuery Flot allows multiple axis, but for each axis, I would need a different set of data, as in this example:
http://people.iola.dk/olau/flot/examples/multiple-axes.html
But I don't want to duplicate the data. Can't I just 'tell' Flot to duplicate the yaxis using the same set of data?
You can use the hooks functionality to force flot to show the second yaxis even though it has no data series assigned to it:
// hook function to mark axis as "used"
// and assign min/max from left axis
pOff = function(plot, offset){
plot.getYAxes()[1].used = true;
plot.getYAxes()[1].datamin = plot.getYAxes()[0].datamin;
plot.getYAxes()[1].datamax = plot.getYAxes()[0].datamax;
}
$.plot("#placeholder2", [ { data: d2 } ], {
hooks: { processOffset: [pOff] },
yaxes: [ {},
{position: 'right'} // add second axis
]
});
Depending on how your axis is configured though, this might be messy. You'll have to steal parameters from the left axis to get it to work (as I've done above with datamin/datamax).
If it was my code, I'd go with your duplicate data approach. You aren't really duplicating anything, just assigned the same array to two series. I'd then configure the 2nd series to simply not draw.
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
// use the same data but toggle off the lines...
$.plot("#placeholder", [ { data: d2 }, {data: d2, yaxis: 2, lines: {show: false}} ], {
yaxes: [ {},
{position: 'right'} ]
});
Here's a fiddle demonstrating the two approaches.