Razor chart.js labels/data not in sync - razor

I have a Razor application that generates three columns of data to use in a chart graph. The page and javascript to do that looks like this:
<div><canvas id="myChart"></canvas></div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var Maanden = [];
var Totalen = [];
#foreach (var m in Model.Grafieks)
{
#:Maanden.push("#m.maand" + "-" + "#m.jaar");
#:Totalen.push(#m.Total);
}
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: Maanden,
datasets: [
{ label: 'Facturen €',
data: Totalen,
backgroundColor: 'rgb(255, 255, 132)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
}
]
},
});
</script>
Problem is that the labels are displayed OK but the data is off. Every second column is empty and its data pushed to the next column:
Chrome says:
Is there something wrong pushing the data into the arrays?

I had to convert the comma in decimal Totalen to a period!
#foreach (var m in Model.Grafieks)
{
#:Maanden.push("#m.maand" + "-" + "#m.jaar");
<text>bedrag = parsePotentiallyGroupedFloat("#m.Total");</text>
#:Totalen.push(bedrag);
}
function parsePotentiallyGroupedFloat(stringValue) {
stringValue = stringValue.trim();
var result = stringValue.replace(/[^0-9]/g, '');
if (/[,\.]\d{2}$/.test(stringValue)) {
result = result.replace(/(\d{2})$/, '.$1');
}
return parseFloat(result);
}
The function "parsePotentiallyGroupedFloat" is from here: Convert String with Dot or Comma as decimal separator to number in JavaScript

Related

Is it possible that we can add dynamic borders to X axis labels and sub labels in chart.js?

I need to create dynamic borders to labels and sub labels of chart.js graph. Is there any way of achieving this scenario?.
Below is the output I am expecting.
Labels and sub labels are having borders
Has anyone been able to achieve this with the library?
Thanks in advance.
I have tried this code but unable to get borders to labels.
var data = [{"omlPartgroupid":"191 ","xmonth":"10","xyear":"2015","QTY":"52"},{"omlPartgroupid":"191 ","xmonth":"11","xyear":"2015","QTY":"145"},{"omlPartgroupid":"191 ","xmonth":"12","xyear":"2015","QTY":"122"},{"omlPartgroupid":"191 ","xmonth":"1","xyear":"2016","QTY":"348"},{"omlPartgroupid":"191 ","xmonth":"2","xyear":"2016","QTY":"460"},{"omlPartgroupid":"191 ","xmonth":"3","xyear":"2016","QTY":"187"},{"omlPartgroupid":"191 ","xmonth":"4","xyear":"2016","QTY":"109"},{"omlPartgroupid":"191 ","xmonth":"5","xyear":"2016","QTY":"234"},{"omlPartgroupid":"191 ","xmonth":"6","xyear":"2016","QTY":"166"},{"omlPartgroupid":"191 ","xmonth":"7","xyear":"2016","QTY":"186"},{"omlPartgroupid":"191 ","xmonth":"8","xyear":"2016","QTY":"250"},{"omlPartgroupid":"191 ","xmonth":"9","xyear":"2016","QTY":"1077"},{"omlPartgroupid":"191 ","xmonth":"10","xyear":"2016","QTY":"594"},{"omlPartgroupid":"193 ","xmonth":"10","xyear":"2015","QTY":"39"},{"omlPartgroupid":"193 ","xmonth":"11","xyear":"2015","QTY":"183"},{"omlPartgroupid":"193 ","xmonth":"12","xyear":"2015","QTY":"136"},{"omlPartgroupid":"193 ","xmonth":"1","xyear":"2016","QTY":"212"},{"omlPartgroupid":"193 ","xmonth":"2","xyear":"2016","QTY":"460"},{"omlPartgroupid":"193 ","xmonth":"3","xyear":"2016","QTY":"176"},{"omlPartgroupid":"193 ","xmonth":"4","xyear":"2016","QTY":"187"},{"omlPartgroupid":"193 ","xmonth":"5","xyear":"2016","QTY":"174"},{"omlPartgroupid":"193 ","xmonth":"6","xyear":"2016","QTY":"151"},{"omlPartgroupid":"193 ","xmonth":"7","xyear":"2016","QTY":"164"},{"omlPartgroupid":"193 ","xmonth":"8","xyear":"2016","QTY":"237"},{"omlPartgroupid":"193 ","xmonth":"9","xyear":"2016","QTY":"798"},{"omlPartgroupid":"193 ","xmonth":"10","xyear":"2016","QTY":"662"},{"omlPartgroupid":"195 ","xmonth":"10","xyear":"2015","QTY":"9"},{"omlPartgroupid":"195 ","xmonth":"11","xyear":"2015","QTY":"38"},{"omlPartgroupid":"195 ","xmonth":"12","xyear":"2015","QTY":"35"},{"omlPartgroupid":"195 ","xmonth":"1","xyear":"2016","QTY":"68"},{"omlPartgroupid":"195 ","xmonth":"2","xyear":"2016","QTY":"161"},{"omlPartgroupid":"195 ","xmonth":"3","xyear":"2016","QTY":"73"},{"omlPartgroupid":"195 ","xmonth":"4","xyear":"2016","QTY":"69"},{"omlPartgroupid":"195 ","xmonth":"5","xyear":"2016","QTY":"56"},{"omlPartgroupid":"195 ","xmonth":"6","xyear":"2016","QTY":"55"},{"omlPartgroupid":"195 ","xmonth":"7","xyear":"2016","QTY":"50"},{"omlPartgroupid":"195 ","xmonth":"8","xyear":"2016","QTY":"114"},{"omlPartgroupid":"195 ","xmonth":"9","xyear":"2016","QTY":"1046"},{"omlPartgroupid":"195 ","xmonth":"10","xyear":"2016","QTY":"883"},{"omlPartgroupid":"197 ","xmonth":"10","xyear":"2015","QTY":"34"},{"omlPartgroupid":"197 ","xmonth":"11","xyear":"2015","QTY":"76"},{"omlPartgroupid":"197 ","xmonth":"12","xyear":"2015","QTY":"114"},{"omlPartgroupid":"197 ","xmonth":"1","xyear":"2016","QTY":"173"},{"omlPartgroupid":"197 ","xmonth":"2","xyear":"2016","QTY":"327"},{"omlPartgroupid":"197 ","xmonth":"3","xyear":"2016","QTY":"134"},{"omlPartgroupid":"197 ","xmonth":"4","xyear":"2016","QTY":"125"},{"omlPartgroupid":"197 ","xmonth":"5","xyear":"2016","QTY":"200"},{"omlPartgroupid":"197 ","xmonth":"6","xyear":"2016","QTY":"104"},{"omlPartgroupid":"197 ","xmonth":"7","xyear":"2016","QTY":"99"},{"omlPartgroupid":"197 ","xmonth":"8","xyear":"2016","QTY":"191"},{"omlPartgroupid":"197 ","xmonth":"9","xyear":"2016","QTY":"845"},{"omlPartgroupid":"197 ","xmonth":"10","xyear":"2016","QTY":"578"},{"omlPartgroupid":"199 ","xmonth":"10","xyear":"2015","QTY":"35"},{"omlPartgroupid":"199 ","xmonth":"11","xyear":"2015","QTY":"75"},{"omlPartgroupid":"199 ","xmonth":"12","xyear":"2015","QTY":"76"},{"omlPartgroupid":"199 ","xmonth":"1","xyear":"2016","QTY":"105"},{"omlPartgroupid":"199 ","xmonth":"2","xyear":"2016","QTY":"229"},{"omlPartgroupid":"199 ","xmonth":"3","xyear":"2016","QTY":"147"},{"omlPartgroupid":"199 ","xmonth":"4","xyear":"2016","QTY":"73"},{"omlPartgroupid":"199 ","xmonth":"5","xyear":"2016","QTY":"50"},{"omlPartgroupid":"199 ","xmonth":"6","xyear":"2016","QTY":"58"},{"omlPartgroupid":"199 ","xmonth":"7","xyear":"2016","QTY":"103"},{"omlPartgroupid":"199 ","xmonth":"8","xyear":"2016","QTY":"4230"},{"omlPartgroupid":"199 ","xmonth":"9","xyear":"2016","QTY":"2570"},{"omlPartgroupid":"199 ","xmonth":"10","xyear":"2016","QTY":"730"}];
var omlPartgroupid =[];
var xyear =[];
var xmonth =[];
var QTY=[];
var labelData = [];
for(var i in data){
omlPartgroupid.push("PartGroup"+ data[i].omlPartgroupid);
xyear.push(data[i].xyear);
xmonth.push(data[i].xmonth);
QTY.push(data[i].QTY);
labelData.push(data[i].xmonth + "|" + data[i].xyear + "|" + data[i].omlPartgroupid);
}
var chartdata ={
labels:labelData,
datasets :[
{
backgroundColor: 'rgba(200,200,200,0.75)',
borderColor: 'rgba(200,200,200,0.75)',
hoverBackground: 'rgba(200,200,200,1)',
hoverBorderColor: 'rgba(200,200,200,1)',
xAxisID:'time',
data:QTY
}
]
};
var ctx=$("#mycanvas");
var barGraph =new Chart(ctx,{
type: 'bar',
data: chartdata,
options: {
scales: {
xAxes:[ {
id: 'time',
type: 'category',
ticks: {
callback: function(label) {
var labelArray = label.split("|");
return labelArray[0] + "/" + labelArray[1];
}
}
},
{
id: 'partGroup',
type: 'category',
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks: {
display:true,
callback: function(label) {
var labelArray = label.split("|");
return labelArray[0] === "10" && labelArray[1] == "2015" ? labelArray[2] : "";
}
}
}
]
}
}
});
<canvas id="mycanvas">

node js mysql query result rendered to chart.js labels in pug page

I've this strange behaviour when i make a get request. A query to mysql calls for totals of sells(float) group by days (nvarchar). I've made 2 arrays (for totals and datas) where i push the content of the result
router.get('/movmensili', function(req, res ,next){
if(!req.session.user){
return res.redirect('/');
}
executeQuery("SELECT SUM(price) as Totale, Data FROM db10101.10101 group by Data order
by Data", function(error, resmov){
var dateArray = [];
var totaliArray = [] ;
for (var i = 0; i<resmov.length; i++) {
dateArray.push(resmov[i].Data)
}
for (var i = 0; i<resmov.length; i++) {
totaliArray.push(resmov[i].Totale)
}
res.render('movmensili', {title: 'movs', date: (dateArray), totali: totaliArray
});
});
});
console.log(dateArray); //['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05']
console.log(totaliArray); //[ '4.00', '5.50', '3.00', '1.75', null ]
so far so good
once I open my Pug page i got to draw a bar chart with Chart.js
the two arrays used for the chart axes, contains numeric values, no problems for the sell totals, but the xlabels should be strings. So far the xlabes are 2016(=2022 minus 05 minus 01), 2015, 2014 and so on....
canvas#myChart(style='width: 100%; height: 100%; margin: 10 auto')
script.
const xlabels = [#{date}] //[2022-05-01,2022-05-02,2022-05-03,2022-05-04,2022-05-05]
const ydatas = [#{totali}] //[4.00,5.50,3.00,1.75,]
I wasn't able to convert / cast / stringify the x values to get the result needed.
Any suggestions?
David, this worked for me. Are you sure you are passing the labels correctly on render (try date: dateArray instead of date: (dateArray)). I didn't create a render function for this page so hard coded the labels and data arrays:
script.
var labels = ['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05',]
var data = {
labels: labels,
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: ['4.00', '5.50', '3.00', '1.75', null],
}]
};
var config = {type: 'line',data: data,options: {}};
var myChart = new Chart(document.getElementById("myChart"),config);
Not best solution, but it works....
async function GetData()
var xlabel = '#{date}';
var xlabel = xlabel.replace(/"/g, '"');
alert(xlabel);
var xlabel = xlabel.split(",");
alert(xlabel);
for(i = 0; i < xlabel.length; i += 1){
xlabel[i] = xlabel[i];
//alert(numarray[i]);
xlabels.push(xlabel[i]);
}
if I hardcode the labels in:
const xlabels = [#{date}]
instead of importing from page render, everything works fine. It's exactly that the point. The console.log of dateArray is perfectly as I would like to be in the xlabels, while once imported the quotes disappear
console.log(dateArray); // ['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05']
const xlabels = [#{date}]; // [2022-05-01,2022-05-02,2022-05-03,2022-05-04,2022-05-05]

Live update the charts.js graph on a web page using json data received from a remote server

I am working on displaying weather info received from a weather station to display as live chart on my web page. I am using charts.js library to render weather data that's fetched from the weather station as JSON data.
In The code, function loadChart() fetched the json data about one field from weather station i.e. 'Humidity' and passes it (as int) to dspChrt(hum) to render the graph.
The main task to do in dspChrt(hum) method that renders the graph to put the data received from laodChrt() in an array that is updated each minute to use it as parameter to display live weather data as a line graph.
As the weather station updates data each minute, I am using setInterval(loadChart, 60000) method to fetch updated json data each minute.
I am following this tutorial that uses this method I am trying to implement.
[Chart.js] little update example
But it's not working though.
Here is my code:
<html>
<head>
<meta charset="utf-8">
<title>Weather Update</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
function dspChrt(hum[]) { // to be called by loadChart() to render live chart
var ctx = document.getElementById('myChart').getContext('2d');
var N = 10;
for(i=0; i<N; i++)
hum.push(0);
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
datasets: [{
label: 'Humidity',
data: hum, // json value received used in method
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'Temprature',
data: [2, 29, 5, 5, 2, 3, 10],
backgroundColor: "rgba(255,153,0,0.4)"
}]
}
});
}
</script>
<script>
var myVar = setInterval(loadChart, 60000);
function loadChrt() { //fetches json data & calls dspChart() to render graph
var wData, hum;
var requestURL = 'https://cors.io/?http://api.holfuy.com/live/?s=759&pw=h1u5l4kka&m=JSON&tu=C&su=m/s'; //URL of the JSON data
var request = new XMLHttpRequest({
mozSystem: true
}); // create http request
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
wData = JSON.parse(request.responseText);
hum = wData.humidity;
console.log("wData: " + wData);
console.log("hum: " + hum);
dspChrt(hum);
}
}
request.open('GET', requestURL);
request.send(); // send the request
//dspChrt(hum);
}
</script>
</head>
<body onload="loadChart();">
<div class="container">
<h2>Weather Update</h2>
<div>
<canvas id="myChart"></canvas>
</div>
</div>
</body>
</html>
You have typo in the for function name loadChart.
The variable declarations and function definitions are needed to be added before being used.
Reduced the length of array to 7 instead of 10.
Here is updated code snippet. I've added the temperature as well. ;)
var humArray = [];
var temArray = [];
var N = 7;
for (i = 0; i < N; i++) {
humArray.push(0);
temArray.push(0);
}
function loadChart() { //fetches json data & calls dspChart() to render graph
var wData, hum, tem;
var requestURL = 'https://cors.io/?http://api.holfuy.com/live/?s=759&pw=h1u5l4kka&m=JSON&tu=C&su=m/s'; //URL of the JSON data
var request = new XMLHttpRequest({
mozSystem: true
}); // create http request
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
wData = JSON.parse(request.responseText);
hum = wData.humidity;
tem = wData.temperature;
humArray.shift();
humArray.push(hum);
temArray.shift();
temArray.push(tem);
dspChrt(humArray, temArray);
}
}
request.open('GET', requestURL);
request.send(); // send the request
//dspChrt(hum);
}
var myVar = setInterval(loadChart, 60000);
function dspChrt(humArray, temArray) { // to be called by loadChart() to render live chart
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
datasets: [{
label: 'Humidity',
data: humArray, // json value received used in method
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'Temprature',
data: temArray,
backgroundColor: "rgba(255,153,0,0.4)"
}]
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<body onload="loadChart();">
<div class="container">
<h2>Weather Update</h2>
<div>
<canvas id="myChart"></canvas>
</div>
</div>
</body>
Hope it helps.

HighCharts : Xaxis lables misplaced

I want to label xaxis with values from JSON data that is happening but it is printing it at wrong place as shown in screenshot :
Here only seven values are present its labeled in that manner but actual data is plotted by leaving a gap of one. For example at 11th there is value and then again at 13t so total seven values but taking 14 places and label properly taking there first seven places how to match labels and bar graph
Following is my code snippet :
var obj = data[$("#host").val()].stats_vol.result.sectoutput;
var my_data_list = [];
var my_data_list1 = [];
var my_data_list2 = [];
var volumes = [];
for(var key in obj) {
var avg_latency = parseInt(obj[key].avg_latency);
var read_latency = parseInt(obj[key].read_latency);
var write_latency = parseInt(obj[key].write_latency);
console.log(key);
volumes.push(key);
my_data_list.push('Average Latency', parseInt(avg_latency));
my_data_list1.push('Read Latency', parseInt(read_latency));
my_data_list2.push('Write Latency', parseInt(write_latency));
}
$('#graphcontainer3').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Volume Level Latency'
},
yAxis: {
tickInterval: 100,
title: {
text: 'Latency(ms)'
}
},
xAxis: {
categories: volumes,
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:.1f} ms</b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 2
}
},
series: [{
name: 'Average latency',
data: my_data_list,
}, {
name: 'Read Latency',
data: my_data_list1,
}, {
name: 'Write Latency ',
data: my_data_list2,
}]
});
Can anyone help me in this code to make this work fine???
I rectified the problem by myself :) It just I was giving labels two times
var obj = data[$("#host").val()].stats_vol.result.sectoutput;
var my_data_list3 = [];
var my_data_list1 = [];
var my_data_list2 = [];
var volumes = [];
for(var key in obj) {
var avg_latency = parseInt(obj[key].avg_latency);
var read_latency = parseInt(obj[key].read_latency);
var write_latency = parseInt(obj[key].write_latency);
console.log(avg_latency);
volumes.push(key);
my_data_list3.push(parseInt(avg_latency)); //here no need to give label again as it is done my volume(key)
my_data_list1.push( parseInt(read_latency));
my_data_list2.push(parseInt(write_latency));
}
This is what I was expecting as output .Hope it might be helpful for someone else hence answered.

Chart.js dynamic bar width

I have a requirement to render a set of time series data of contiguous blocks.
I need to describe a series of bars which could span many hours, or just minutes, with their own Y value.
I'm not sure if ChartJS is what I should be using for this, but I have looked at extending the Bar type, but it seems very hard coded for each bar to be the same width. The Scale Class internally is used for labels, chart width etc, not just the bars themselves.
I am trying to achieve something like this that works in Excel: http://peltiertech.com/variable-width-column-charts/
Has anyone else had to come up with something similar?
I found I needed to do this and the answer by #potatopeelings was great, but out of date for version 2 of Chartjs. I did something similar by creating my own controller/chart type via extending bar:
//controller.barw.js
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.barw = {
hover: {
mode: 'label'
},
scales: {
xAxes: [{
type: 'category',
// Specific to Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
type: 'linear'
}]
}
};
Chart.controllers.barw = Chart.controllers.bar.extend({
/**
* #private
*/
getRuler: function() {
var me = this;
var scale = me.getIndexScale();
var options = scale.options;
var stackCount = me.getStackCount();
var fullSize = scale.isHorizontal()? scale.width : scale.height;
var tickSize = fullSize / scale.ticks.length;
var categorySize = tickSize * options.categoryPercentage;
var fullBarSize = categorySize / stackCount;
var barSize = fullBarSize * options.barPercentage;
barSize = Math.min(
helpers.getValueOrDefault(options.barThickness, barSize),
helpers.getValueOrDefault(options.maxBarThickness, Infinity));
return {
fullSize: fullSize,
stackCount: stackCount,
tickSize: tickSize,
categorySize: categorySize,
categorySpacing: tickSize - categorySize,
fullBarSize: fullBarSize,
barSize: barSize,
barSpacing: fullBarSize - barSize,
scale: scale
};
},
/**
* #private
*/
calculateBarIndexPixels: function(datasetIndex, index, ruler) {
var me = this;
var scale = ruler.scale;
var options = scale.options;
var isCombo = me.chart.isCombo;
var stackIndex = me.getStackIndex(datasetIndex);
var base = scale.getPixelForValue(null, index, datasetIndex, isCombo);
var size = ruler.barSize;
var dataset = me.chart.data.datasets[datasetIndex];
if(dataset.weights) {
var total = dataset.weights.reduce((m, x) => m + x, 0);
var perc = dataset.weights[index] / total;
var offset = 0;
for(var i = 0; i < index; i++) {
offset += dataset.weights[i] / total;
}
var pixelOffset = Math.round(ruler.fullSize * offset);
var base = scale.isHorizontal() ? scale.left : scale.top;
base += pixelOffset;
size = Math.round(ruler.fullSize * perc);
size -= ruler.categorySpacing;
size -= ruler.barSpacing;
}
base -= isCombo? ruler.tickSize / 2 : 0;
base += ruler.fullBarSize * stackIndex;
base += ruler.categorySpacing / 2;
base += ruler.barSpacing / 2;
return {
size: size,
base: base,
head: base + size,
center: base + size / 2
};
},
});
};
Then you need to add it to your chartjs instance like this:
import Chart from 'chart.js'
import barw from 'controller.barw'
barw(Chart); //add plugin to chartjs
and finally, similar to the other answer, the weights of the bar widths need to be added to the data set:
var data = {
labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.7)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 30, 56, 65, 40],
weights: [1, 0.9, 1, 2, 1, 4, 0.3]
},
]
};
This will hopefully get someone onto the right track. What I have certainly isn't perfect, but if you make sure you have the right number of weight to data points, you should be right.
Best of luck.
This is based on the #Shane's code, I just posted to help, since is a common question.
calculateBarIndexPixels: function (datasetIndex, index, ruler) {
const options = ruler.scale.options;
const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options) : computeFitCategoryTraits(index, ruler, options);
const barSize = range.chunk;
const stackIndex = this.getStackIndex(datasetIndex, this.getMeta().stack);
let center = range.start + range.chunk * stackIndex + range.chunk / 2;
let size = range.chunk * range.ratio;
let start = range.start;
const dataset = this.chart.data.datasets[datasetIndex];
if (dataset.weights) {
//the max weight should be one
size = barSize * dataset.weights[index];
const meta = this.chart.controller.getDatasetMeta(0);
const lastModel = index > 0 ? meta.data[index - 1]._model : null;
//last column takes the full bar
if (lastModel) {
//start could be last center plus half of last column width
start = lastModel.x + lastModel.width / 2;
}
center = start + size * stackIndex + size / 2;
}
return {
size: size,
base: center - size / 2,
head: center + size / 2,
center: center
};
}
For Chart.js you can create a new extension based on the bar class to do this. It's a bit involved though - however most of it is a copy paste of the bar type library code
Chart.types.Bar.extend({
name: "BarAlt",
// all blocks that don't have a comment are a direct copy paste of the Chart.js library code
initialize: function (data) {
// the sum of all widths
var widthSum = data.datasets[0].data2.reduce(function (a, b) { return a + b }, 0);
// cumulative sum of all preceding widths
var cumulativeSum = [ 0 ];
data.datasets[0].data2.forEach(function (e, i, arr) {
cumulativeSum.push(cumulativeSum[i] + e);
})
var options = this.options;
// completely rewrite this class to calculate the x position and bar width's based on data2
this.ScaleClass = Chart.Scale.extend({
offsetGridLines: true,
calculateBarX: function (barIndex) {
var xSpan = this.width - this.xScalePaddingLeft;
var x = this.xScalePaddingLeft + (cumulativeSum[barIndex] / widthSum * xSpan) - this.calculateBarWidth(barIndex) / 2;
return x + this.calculateBarWidth(barIndex);
},
calculateBarWidth: function (index) {
var xSpan = this.width - this.xScalePaddingLeft;
return (xSpan * data.datasets[0].data2[index] / widthSum);
}
});
this.datasets = [];
if (this.options.showTooltips) {
Chart.helpers.bindEvents(this, this.options.tooltipEvents, function (evt) {
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
this.eachBars(function (bar) {
bar.restore(['fillColor', 'strokeColor']);
});
Chart.helpers.each(activeBars, function (activeBar) {
activeBar.fillColor = activeBar.highlightFill;
activeBar.strokeColor = activeBar.highlightStroke;
});
this.showTooltip(activeBars);
});
}
this.BarClass = Chart.Rectangle.extend({
strokeWidth: this.options.barStrokeWidth,
showStroke: this.options.barShowStroke,
ctx: this.chart.ctx
});
Chart.helpers.each(data.datasets, function (dataset, datasetIndex) {
var datasetObject = {
label: dataset.label || null,
fillColor: dataset.fillColor,
strokeColor: dataset.strokeColor,
bars: []
};
this.datasets.push(datasetObject);
Chart.helpers.each(dataset.data, function (dataPoint, index) {
datasetObject.bars.push(new this.BarClass({
value: dataPoint,
label: data.labels[index],
datasetLabel: dataset.label,
strokeColor: dataset.strokeColor,
fillColor: dataset.fillColor,
highlightFill: dataset.highlightFill || dataset.fillColor,
highlightStroke: dataset.highlightStroke || dataset.strokeColor
}));
}, this);
}, this);
this.buildScale(data.labels);
// remove the labels - they won't be positioned correctly anyway
this.scale.xLabels.forEach(function (e, i, arr) {
arr[i] = '';
})
this.BarClass.prototype.base = this.scale.endPoint;
this.eachBars(function (bar, index, datasetIndex) {
// change the way the x and width functions are called
Chart.helpers.extend(bar, {
width: this.scale.calculateBarWidth(index),
x: this.scale.calculateBarX(index),
y: this.scale.endPoint
});
bar.save();
}, this);
this.render();
},
draw: function (ease) {
var easingDecimal = ease || 1;
this.clear();
var ctx = this.chart.ctx;
this.scale.draw(1);
Chart.helpers.each(this.datasets, function (dataset, datasetIndex) {
Chart.helpers.each(dataset.bars, function (bar, index) {
if (bar.hasValue()) {
bar.base = this.scale.endPoint;
// change the way the x and width functions are called
bar.transition({
x: this.scale.calculateBarX(index),
y: this.scale.calculateY(bar.value),
width: this.scale.calculateBarWidth(index)
}, easingDecimal).draw();
}
}, this);
}, this);
}
});
You pass in the widths like below
var data = {
labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.7)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 30, 56, 65, 40],
data2: [10, 20, 30, 20, 10, 40, 10]
},
]
};
and you call it like so
var ctx = document.getElementById('canvas').getContext('2d');
var myLineChart = new Chart(ctx).BarAlt(data);
Fiddle - http://jsfiddle.net/moye0cp4/