Negative bar chart of highcharts in chartjs - highcharts

I been playing with Chartjs to generate something like highcharts but couldn't bring it close. The application is finished and it seems a waste to buy highcharts, migrate from chartjs for this single chart.
Is there a way Chartjs can output something like that? I did try creating two horizontal bar charts side-by-side in separate canvas; while i saw the output; the behavior is not what I want.

You could use a horizontal stacked barchart with two datasets. Add negative values to the dataset on the left (male) but display the absolute value in the callbacks of the xAxis and the tooltip.
var ctx = document.getElementById('myChart').getContext('2d');
var data = {
labels: ["20-30", "10-20", "0-10"],
datasets: [{
label: "Male",
backgroundColor: "rgba(54, 162, 235, 0.2)",
borderColor: "rgb(54, 162, 235)",
borderWidth: 2,
data: [-65, -59, -20],
}, {
label: "Female",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 2,
data: [72, 45, 18],
},
]
};
var myBarChart = new Chart(ctx, {
type: 'horizontalBar',
data: data,
options: {
scales: {
yAxes: [{
stacked: true
}],
xAxes: [{
ticks: {
callback: function(value, index, values) {
return Math.abs(value);
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return data.datasets[tooltipItems.datasetIndex].label + ": " + Math.abs(tooltipItems.xLabel);
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<canvas id="myChart"></canvas>

Related

call chartjs option on rails chartkick

I am implementing charts in our sites, Here using chartkick plugin. But some customization is not supported by chartkick, But we can use chartjs, highchart and google chart by using import options indirectly. chartkick is support the above charts in its configuration.
So we choose chartjs, Here we are customizing legend and tooltip. Found the options in chartjs but unable to implement in chartkick helper.
Need your help guys, here i am facing issue like unable to mention the customization options in rails tag. Because found customization options by using javascript, but i need to add this options inside rails tag<%= %>
These are things have used for implementation.
chart js version 2.9.4
added chartkick by added gem 'chartkick' to gemfile
added chartkick, require Chart.bundle
using chartjs inside chartkick by use adapter option to the chartkick helper
<%= area_chart data, library: {
legend: {
position: 'right',
labels: {
boxWidth: 15,
boxHeight: 15
}
}
}, id: 'consult-chart', adapter: 'chartjs' %>
Found the options for customizing legend and tooltip.
Added customization in legend is -> added few extra label to normal label
Added customization in tooltip is -> changed background color and added extra text.
The below one is the options for above customization by use of direct chart js implementaion.
<script>
var ctx = document.getElementById("myChart-total");
var myLegendContainer = document.getElementById("legends");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["0", "5", "10", "15", "20", "25"],
datasets: [
{
label: "Revenue",
lineTension: 0,
backgroundColor : "#e969e5",
borderColor : "#d93da9",
//backgroundColor: "#d93da9",
data: [0, 10, 23, 25, 60, 70]
}
]
},
options: {
layout: {
padding: {
bottom: 100
}
},
legend: {
position: 'right',
labels : {
generateLabels: function(chart){
var data = chart.data;
var legends = Array.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
return {
text: dataset.label,
fillStyle: (!Array.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
hidden: !chart.isDatasetVisible(i),
lineCap: dataset.borderCapStyle,
lineDash: dataset.borderDash,
lineDashOffset: dataset.borderDashOffset,
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
pointStyle: dataset.pointStyle,
// Below is extra data used for toggling the datasets
datasetIndex: i
};
}, this) : [];
var title_o = {
text: 'Past 6 months: $826.53',
strokeStyle: 'transparent',
fillStyle: 'transparent',
lineWidth: 0
};
var title_tw = {
text: 'Month-to-date: $67.91',
strokeStyle: 'transparent',
fillStyle: 'transparent',
lineWidth: 0
};
var title_th = {
text: 'Past 7 days: $26.53',
strokeStyle: 'transparent',
fillStyle: 'transparent',
lineWidth: 0
};
legends.push(title_o);
legends.push(title_tw);
legends.push(title_th);
return legends;
},
boxWidth: 15,
boxHeight: 20
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true
},
}],
xAxes: [{
gridLines: {
color: "rgba(0, 0, 0, 0)",
}
}]
},
tooltips: {
yAlign: 'bottom',
callbacks: {
title: function(tooltipItem, data) {
return "September "+data['labels'][tooltipItem[0]['index']];
},
label: function(tooltipItem, data) {
return "$"+data['datasets'][0]['data'][tooltipItem['index']];
},
},
backgroundColor: 'grey'
}
}
});
Here how can add the options for legend and tooltip
For legend customization, this option should be mention inside rails tag =>
legend: {
position: 'right',
labels : {
generateLabels: function(chart){
var data = chart.data;
var legends = Array.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
return {
text: dataset.label,
fillStyle: (!Array.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
hidden: !chart.isDatasetVisible(i),
lineCap: dataset.borderCapStyle,
lineDash: dataset.borderDash,
lineDashOffset: dataset.borderDashOffset,
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
pointStyle: dataset.pointStyle,
datasetIndex: i
};
}, this) : [];
var title_o = {
text: 'Past 6 months: $826.53',
strokeStyle: 'transparent',
fillStyle: 'transparent',
lineWidth: 0
};
legends.push(title_o);
return legends;
},
boxWidth: 15,
boxHeight: 20
}
},
For tooltip customization, this option should be mention inside rails tag =>
tooltips: {
yAlign: 'bottom',
callbacks: {
title: function(tooltipItem, data) {
return "September "+data['labels'][tooltipItem[0]['index']];
},
label: function(tooltipItem, data) {
return "$"+data['datasets'][0]['data'][tooltipItem['index']];
},
},
backgroundColor: 'grey'
}
==========================================-------------------=========================
But how i need to mention the above options in rails tag, because rails tag no supporting javascript function.
I added inside the quotes, but its not working, added like as below
<%= area_chart data, library: {
layout: {
padding: {
bottom: 1
}
},
legend: {
position: 'right',
labels: {
boxWidth: 15,
boxHeight: 15,
generateLabels: 'function(chart){
var data = chart.data;
var legends = Array.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
return {
text: dataset.label,
fillStyle: (!Array.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
hidden: !chart.isDatasetVisible(i),
lineCap: dataset.borderCapStyle,
lineDash: dataset.borderDash,
lineDashOffset: dataset.borderDashOffset,
lineJoin: dataset.borderJoinStyle,
lineWidth: dataset.borderWidth,
strokeStyle: dataset.borderColor,
pointStyle: dataset.pointStyle,
// Below is extra data used for toggling the datasets
datasetIndex: i
};
}, this) : [];
var title_o = {
text: "Past 6 months: $826.53",
strokeStyle: "transparent",
fillStyle: "transparent",
lineWidth: 0
};
legends.push(title_o);
return legends;
}',
}
},
tooltips: {
yAlign: 'bottom',
callbacks: "{
title: function(tooltipItem, data) {
return data['labels'][tooltipItem[0]['index']];
},
label: function(tooltipItem, data) {
return data['datasets'][0]['data'][tooltipItem['index']];
},
}",
backgroundColor: 'grey'
}
}, id: 'consult-chart', adapter: 'chartjs', width: "500px", height: "341px" %>
Please help me here, how to implement these customization options?

xAxis Image Disappears in Highcharts After First Refresh

I have a page with a variety of select menus. The select options are used in an ajax call to build a Highcharts bar graph. Every time a filter changes, the graph gets recreated. I did this instead of updating the series data, because in the past I have noticed that destroying and recreating was more efficient than updating.
I want images to show on the x-axis, so I used a nice little trick of creating two x axes, used formatter to return an image on the first axis, and linked the second axis to the first. This works on first refresh. However, every time the chart gets recreated thereafter, the image disappears. I checked my console and I don't see any errors.
And idea of what's going on here?
/**
* Whenselection changes
*/
$(document).on('change', '.filter', function(){
getChartData($params)
})
});
/**
* API call to get data that will populate charts.
* #param {obj} params
*/
function getChartData(params)
{
//Get chart data
$.ajax({
url: apiURL + '/chartdata/',
data: params,
dataType: 'json',
cache: false,
success: function(data) {
initChart(data[0]);
}
});
function initChart(chartData)
{
var chart = Highcharts.chart('container', {
chart: {
type: 'bar',
backgroundColor: 'transparent', //#E8EAF6',
height: '23%',
marginLeft: 35
},
title: {
text: null
},
xAxis: {
categories: [category1, category2],
lineColor: 'transparent',
min: 0,
tickColor: 'transparent',
title: {
text: null
},
labels: {
x: -35,
useHTML: true,
overflow: 'allow',
formatter: function () {
if(this.isFirst == true)
return '<img src="../assets/img/nat-jr-grad-gold.png"><br/>';
else
return '<img src="../assets/img/nat-jr-grad-purple.png"><br/>';
}
}
},
yAxis: {
min: 0,
title: {
useHTML: true,
text: null
},
labels: {
enabled: false
},
lineWidth: 0,
minorGridLineWidth: 0,
gridLineWidth: 0,
lineColor: 'transparent',
gridLineColor: 'transparent',
},
legend: {
enabled: false
},
series: [{
name: category1,
data: [{name: category1, y:Math.round(chartData.p_grad_nongap * 100), {y: null}],
}, {
name: category2,
data: [null, {name: category2, y: Math.round(chartData.p_grad_gap * 100)}]
}]
});
}
I reproduced your problem on a simplified example: http://jsfiddle.net/BlackLabel/sm2r684n/
For the first time, the image is loaded asynchronously and the chart does not take it into account when calculating the margins. Every next time the result is different, so you should wait until the picture is loaded:
var img = new Image();
img.onload = function() {
initChart();
}
img.src = "https://www.highcharts.com/samples/graphics/sun.png";
Live demo: http://jsfiddle.net/BlackLabel/m09ok2cg/
I think ppotaczek had the cortrect root cause of the problem; the image is loaded asynchronously and the chart does not take it into account when calculating the margins. His suggestion used setTimeout function to continuously redraw the graph, which is rather inefficient. My work-around for this was to just add the images as avg elements using chart.renderer after the chart was created.
/* Render X-Axis images */
chart.renderer.image('../assets/img/img1.png', 0, 40, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();
chart.renderer.image('../assets/img/img2.png', 0, 130, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();

How to extend a line series as dotted only when checkbox is clicked?

I have a line series from say 0-t1 interval of x.
i want to show an extended dashed line from t1-t2(in the same series) if user click on the checkup next to legend.
Can anyone suggest how to go about it?
You should be able to use checkboxClick function for updating your series on checkbox click:
http://api.highcharts.com/highcharts/plotOptions.series.events.checkboxClick
http://api.highcharts.com/highcharts/Series.update
You can use zones for changing style of your line from some value:
http://api.highcharts.com/highcharts/plotOptions.series.zones
series: [{
data: [-10, -5, 0, 5, 10, 15, 10, 10, 5, 0, -5],
showCheckbox: true,
events: {
checkboxClick: function() {
var checkbox = this.checkbox;
var newData = $.extend([], true, data);
for (var i = 0; i < 14; i++) {
newData.push(20 * Math.random());
}
if (checkbox.checked) {
this.update({
zones: [{
value: data.length - 1,
}, {
dashStyle: 'dash'
}],
selected: false,
zoneAxis: 'x',
data: newData
})
} else {
this.update({
zones: [],
data: data,
selected: true
});
}
}
}
}]
Live example of a chart with zones:
http://jsfiddle.net/yavvLvea/3/

Change HighCharts plotLine-value and -label

I have implemented a plot line that's showing an average of all values:
"yAxis": {
plotLines: [{
color: 'red',
value: 22,
width: '1',
label: {
text: 'Average: 22'
}
}]
},
I want to change the average-value on legendItemClick. Is it possible to change some plotLine-properties programatically?
What I tried so far is this:
plotOptions: {
series: {
events: {
legendItemClick: function (event) {
var average = 15;
event.target.chart.userOptions.yAxis.plotLines[0].value = average;
event.target.chart.userOptions.yAxis.plotLines[0].label.text = 'Average: ' + average;
}
},
}
},
The value and the label-text aren't changing in the chart. Also I tried to redraw the chart but it still isn't changing the plot line. Is there a way to accomplish this?
I now have found an acceptable answer to my own question:
var average = 15;
event.target.chart.yAxis[0].removePlotLine('average');
event.target.chart.yAxis[0].addPlotLine({
id: 'average',
color: 'red',
width: 1,
value: average,
zIndex: 200,
label: {
text: 'Average: ' + average
}
});
I have to specify an ID for the plotLine, destroy it and create a new one.

Spiderweb Highcharts with multiple scales on multiple axes

My requirement is to create a spiderweb highchart with values on each axis where each axes have different scales.Is it possible? I have attached a same output of my requirement.
Use multiple yAxis and assign them to the separated panes with startAngle.
Parser
var colors = Highcharts.getOptions().colors,
each = Highcharts.each,
series = [{
yAxis: 0,
data: [10, 20, 30]
}, {
yAxis: 1,
data: [1, 2, 3]
}, {
yAxis: 2,
data: [4, 2, 1]
}, {
yAxis: 3,
data: [5, 1, 3]
}, {
yAxis: 4,
data: [2, 3, 4]
}],
yAxis = [],
panes = [],
startAngle = 0;
each(series, function(serie, i) {
yAxis.push({
pane: i,
showLastLabel: true,
gridLineWidth: i === 0 ? true : false,
labels: {
useHTML: true,
formatter: function() {
return '<span style="color:' + colors[i] + '">' + this.value + '</span>';
}
}
});
panes.push({
startAngle: startAngle
});
startAngle += 72;
});
Chart configuartion
$('#container').highcharts({
/*
chart options
*/
pane: panes,
yAxis: yAxis,
series: series
});
Example:
http://jsfiddle.net/6jmqb1r8/
I believe Sebastian Bochan's answer is rock solid due to his suggestion of the pane attributes. However, I was tinkering around with another method and wanted to share it with you and the community.
My solution makes use of "dummy" series, which are series that the user does not see or interact with, but can help with customized features such as your labels.
I added six "dummy" series that contain the labels for each spoke of the spider chart. The first, for the "zero" value, is blank, but the others will show data labels for the first, second, third, etc. points along the spoke.
After the chart is drawn, I use the addSeries() function to add these "dummy" series to the chart:
// Add "dummy series to control the custom labels.
// We will add one for each spoke, but first will always be
// overriden by y-axis labels; I could not figure out how to
// disable that behavior.
// The color, showInLegend, and enableMouseTracking attributes
// prevent the user from seeing or interacting with the series
// as they are only used for the custom labels.
var chart = $('#container').highcharts();
var labelArray = [
['','1','2','3','4','5'],
['','j','k','l','m','n'],
['','one','two','three','four','five'],
['','u','v','w','x','y'],
['','a','b','c','d','e']
];
for (var i = 0; i<=5; i++) {
chart.addSeries({
name: 'dummy series #' + i + ' for label placement',
data: [
{ name: labelArray[0][i], y: i },
{ name: labelArray[1][i], y: i },
{ name: labelArray[2][i], y: i },
{ name: labelArray[3][i], y: i },
{ name: labelArray[4][i], y: i }
],
dataLabels: {
enabled: true, padding: 0, y: 0,
formatter: function() {
return '<span style="font-weight: normal;">' + this.point.name + '</span>';
}
},
pointPlacement: 'on',
lineWidth: 0,
color: 'transparent',
showInLegend: false,
enableMouseTracking: false
});
}
A few items to note:
lineWidth: 0 and color: 'transparent' makes the "dummy" series lines invisible
showInLegend: false prevents them from showing up in the legend
enableMouseTracking: false prevents the users from interacting with them
Here is the fiddle that shows how this works: http://jsfiddle.net/brightmatrix/944d2p6q/
The result looks like this:
Just on quirk that I noted in my comments: I could not figure out how to override the labels on the first spoke (at the 12-o-clock position). If I set the y-axis labels to "false," it refused to show anything, even the custom data labels I set in the "dummy" series. Therefore, I suggest that your first spoke be the numerical labels in your example.
I realize this is perhaps a more complicated route, but I hope it's helpful to you and others in some way.

Resources