Related
Is it possible to add some shadow while hovering an index in chartJS?
Somethink like in this answer which add a line when points are hovered but i would to use it with a bar chart and make it look something like this:
Here is my actual chart jsfiddle
<div class="chart-area chart-reparti">
<canvas id="chartReparti" width="1600" height="250"></canvas>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
var optionsReparti = {
maintainAspectRatio: false,
legend: {
display: true
},
tooltips: {
backgroundColor: '#f5f5f5',
titleFontColor: '#333',
bodyFontColor: '#666',
displayColors: true,
mode: 'index',
intersect: 0
},
responsive: true,
scales: {
yAxes: [{
id: 'importo',
gridLines: {
drawBorder: false,
borderDash: [4, 8],
color: 'rgba(0,132,255,0.4)',
},
ticks: {
beginAtZero: true,
userCallback: function (value, index, values) {
return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
}
}, {
id: 'qta',
gridLines: {
drawBorder: false,
borderDash: [4, 8],
color: 'rgba(247,136,0,0.4)',
},
ticks: {
beginAtZero: true
}
},
],
xAxes: [{
categoryPercentage: 1,
barPercentage: 0.4,
gridLines: {
drawBorder: false,
color: 'rgba(225,78,202,0.1)',
zeroLineColor: "transparent",
}
}]
}
};
var ctx = document.getElementById("chartReparti").getContext('2d');
var gradientImporto = ctx.createLinearGradient(0, 170, 0, 50);
gradientImporto.addColorStop(0, "rgba(0, 98, 255, 1)");
gradientImporto.addColorStop(1, "rgba(84, 150, 255, 1)");
var gradientQuantita = ctx.createLinearGradient(0, 170, 0, 50);
gradientQuantita.addColorStop(0, "rgba(247, 136, 0, 1)");
gradientQuantita.addColorStop(1, "rgba(255, 209, 72, 1)");
var chartReparti = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Cane', 'Gatto', 'Accessori', 'Mangime', 'Carne', 'Cane', 'Gatto', 'Accessori', 'Mangime', 'Carne'],
datasets: [{
label: "Quantità ",
yAxisID: 'qta',
fill: true,
backgroundColor: gradientQuantita,
pointBackgroundColor: '#f78800',
data: [15, 15, 29, 10, 35, 12, 29, 10, 35, 12]
}, {
label: "Importo",
yAxesID: 'importo',
fill: true,
backgroundColor: gradientImporto,
pointBackgroundColor: '#0084ff',
data: [2954, 4564, 2954, 4564, 3456, 4212, 5060, 3456, 4212, 5060]
}
]
},
options: optionsReparti
});
Actually the code of the modified plugin got from this answer is not included as it wasn't working at all.
U can customize code before draw it
Working demo : https://jsfiddle.net/4rasm1hc/
let draw = Chart.controllers.line.prototype.draw;
Chart.controllers.line = Chart.controllers.bar.extend({
draw: function() {
draw.apply(this, arguments);
let ctx = this.chart.chart.ctx;
let _stroke = ctx.stroke;
ctx.stroke = function() {
ctx.save();
ctx.shadowColor = '#000000';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 4;
_stroke.apply(this, arguments)
ctx.restore();
}
}
});
Chart.defaults.LineWithLine = Chart.defaults.bar;
Chart.controllers.LineWithLine = Chart.controllers.bar.extend({
draw: function(ease) {
Chart.controllers.line.prototype.draw.call(this, ease);
if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
var activePoint = this.chart.tooltip._active[0],
ctx = this.chart.ctx,
x = activePoint.tooltipPosition().x+15,
topY =6000,
width=activePoint._view.width,
bottomY = 0;
console.log(activePoint);
// draw line
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x+width-10, bottomY+30);
ctx.lineWidth = width*4;
ctx.strokeStyle = '#e5e0e01a';
ctx.stroke();
ctx.restore();
}
}
});
My Chart.js plot uses two Y-Axis: both of them must have the same scale and max/min values. I update the second Y-Axis step depending on a boolean value I dinamically set and everything is working flawless. Still, I want to update the second Y-Axis (redrawn it) when I trigger a legend event (clicking on one of the legends). I can't really make it work: the plot refresh, but my second Y-Axis step doesn't change.
Here my draw function. I've read the Chart.js documentation I've found here, but I can really understand how to fill my legend onClick event.
function drawChart(pointsArray, curveFeatures) {
let minX = parseFloat(curveFeatures.minX);
let minY = parseFloat(curveFeatures.minY);
let maxX = parseFloat(curveFeatures.maxX);
let maxY = parseFloat(curveFeatures.maxY);
let stepResult = parseInt(curveFeatures.stepResult);
let startX = parseFloat(curveFeatures.startX);
let endX = parseFloat(curveFeatures.endX);
let upperLimit = parseFloat(curveFeatures.upperLimit);
let lowerLimit = parseFloat(curveFeatures.lowerLimit);
let stepSize;
let borderColour;
let backgroundColour;
if (stepResult === 1) {
stepSize = 0.5;
maxY = parseFloat(maxY.toFixed(2)) + stepSize;
minY = parseFloat(minY.toFixed(2)) - stepSize;
borderColour = "rgba(1, 165, 15, 1)";
backgroundColour = "rgba(1, 165, 15, 0.2)";
} else {
stepSize = 0.01;
maxY = parseFloat(maxY.toFixed(2));
minY = parseFloat(minY.toFixed(2));
borderColour = "rgba(252, 45, 45, 1)";
backgroundColour = "rgba(252, 45, 45, 0.2)";
}
let ctx = document.getElementById("myChart").getContext("2d");
let myChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: "Press Signal",
cubicInterpolationMode: "monotone",
fill: false,
borderWidth: 5,
radius: 1,
borderColor: borderColour,
backgroundColor: backgroundColour,
pointBorderColor: borderColour,
pointBackgroundColor: backgroundColour,
data: pointsArray
}, {
label: "Evaluation Windows",
cubicInterpolationMode: "monotone",
fill: true,
borderWidth: 2,
radius: 2,
borderColor: "rgb(94, 233, 255, 1)",
backgroundColor: "rgb(94, 233, 255, 0.2)",
pointBorderColor: "rgb(94, 233, 255, 1)",
pointBackgroundColor: "rgb(94, 233, 255, 0.2)",
data: [{
x: startX,
y: lowerLimit
}, {
x: startX,
y: upperLimit
}, {
x: endX,
y: upperLimit
}, {
x: endX,
y: lowerLimit
}, {
x: startX,
y: lowerLimit
}]
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
animation: {
duration: 2000
},
layout: {
padding: {
left: 50,
right: 50,
top: 50,
bottom: 10
}
},
title: {
display: true,
text: "Press Signal",
fontSize: 30,
padding: 20
},
legend: {
labels: {
fontSize: 16
},
onClick: function(e, legendItem) {
stepSize = 0.01;
// Should I do things, here?
ci.update();
}
},
scales: {
xAxes: [{
display: true,
type: "linear",
position: "bottom",
ticks: {
fontSize: 14,
},
scaleLabel: {
display: true,
fontSize: 16,
fontColor: "black",
labelString: "Position (Abs.) [mm]"
}
}],
yAxes: [{
display: true,
type: "linear",
position: "left",
ticks: {
fontSize: 14,
},
scaleLabel: {
display: true,
fontSize: 16,
fontColor: "black",
labelString: "Force [kN]"
}
}, {
display: true,
type: "linear",
position: "right",
ticks: {
fontSize: 14,
beginAtZero: false,
max: maxY,
min: minY,
stepSize: stepSize
},
afterTickToLabelConversion: function(scaleInstance) {
if (stepResult === 1) {
// set the first and last tick to null so it does not display
// note, ticks[0] is the last tick and ticks[length - 1] is the first
scaleInstance.ticks[0] = null;
scaleInstance.ticks[scaleInstance.ticks.length - 1] = null;
// need to do the same thing for this similiar array which is used internally
scaleInstance.ticksAsNumbers[0] = null;
scaleInstance.ticksAsNumbers[scaleInstance.ticksAsNumbers.length - 1] = null;
}
}
}]
}
}
});
}
I've found my way. Here a snippet for whoever will have my same problem:
onClick: function(event, legendItem) {
let index = 1;
myChart.data.datasets[index].hidden = !myChart.data.datasets[index].hidden;
if (myChart.data.datasets[index].hidden) {
myChart.options.scales.yAxes[index].ticks.max = maxY;
myChart.options.scales.yAxes[index].ticks.min = minY;
myChart.options.scales.yAxes[index].ticks.stepSize = stepSize;
} else {
if (stepResult === 0) {
myChart.options.scales.yAxes[index].ticks.max = 3.5;
myChart.options.scales.yAxes[index].ticks.min = 0;
myChart.options.scales.yAxes[index].ticks.stepSize = 0.5;
}
}
myChart.update();
}
I'm trying to write a chart.js (v.2.7) plugin to show errorbars on my scatter / line plots.
I managed to get the pixel positions from the chart and draw the errorbars on the canvas, but i cannot get the timing to look right. I want them to appear after the lines animate, then stay attached (& move with) if the datasets are hidden->shown.
I've tried:
The afterDraw / afterDataset(s)Draw / beforeDraw hooks: the error bars are already on the plot before the lines animate (as in example). When hidden->shown, the error bars are in place.
afterRender / afterEvent hook: draws them after the animation finishes, but then redraws them everytime the datasets are hidden->shown (after a pause)
beforeRender / any earlier hooks: gives no errorbars
setTimout() on the draw function, or at various places inside it: does nothing
sleep() before the draw function or other places: slows down the whole animation but the errorsbars are unaffected.
I couldn't find a way to invoke plugin functions after animation or get to then via the options.animation.onComplete
Is there a way to get the errorbars to behave as in the example, but the initial appearance occurs after the line animation (with a plugin?)
var errorbarPlugin = {
calcPoints: function(chartInstance, dataList){
var ds = chartInstance.data.datasets
var meta = chartInstance.getDatasetMeta(0)
var yScale = chartInstance.scales[meta.yAxisID];
var xScale = chartInstance.scales[meta.xAxisID];
var yList = []; var xList = [];
for(var i = 0; i < dataList.length; i++){
var yValue = dataList[i].y
var yPixel = yScale.getPixelForValue(yValue)
yList.push(yPixel)
var xValue = dataList[i].x
var xPixel = xScale.getPixelForValue(xValue)
xList.push(xPixel)
}
return {yList: yList, xList: xList}
},
calcErrorbars: function(chartInstance, ds_num){
var ds = chartInstance.data.datasets
var data_list = ds[ds_num].data
var isHidden = ds[ds_num]._meta[Object.keys(chartInstance.data.datasets[ds_num]._meta)[0]].hidden;
var yList = this.calcPoints(chartInstance, data_list).yList
var xList = this.calcPoints(chartInstance, data_list).xList
if(ds[ds_num].errors){
var errors = ds[ds_num].errors
} else {errors = 0}
return [xList, yList, errors, isHidden]
},
drawErrorbars: function(chartInstance){
var ctx = chartInstance.chart.ctx
var ds = chartInstance.data.datasets
for(var ds_num = 0; ds_num < ds.length; ds_num++){
var errCalc = this.calcErrorbars(chartInstance, ds_num)
var isHidden = errCalc[3]
var yList = errCalc[1]
var xList = errCalc[0]
var errors = errCalc[2]
var errWidth = 3
var capLen = 5
if(!isHidden){
for(var k = 0; k < xList.length; k++){
ctx.strokeStyle = "red"
ctx.beginPath();
ctx.moveTo(xList[k], yList[k]-errors[k]);
ctx.lineTo(xList[k], yList[k]+errors[k]);
ctx.moveTo(xList[k]-capLen, yList[k]+errors[k]);
ctx.lineTo(xList[k]+capLen, yList[k]+errors[k]);
ctx.moveTo(xList[k]-capLen, yList[k]-errors[k]);
ctx.lineTo(xList[k]+capLen, yList[k]-errors[k]);
ctx.stroke()
}
}
}
},
afterDatasetsDraw: function(chartInstance) {
this.drawErrorbars(chartInstance)
},
}
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>
<script type="text/javascript" src="{% static 'js/charts/errorbarPlugin3.js' %}"></script>
<div id="canvas-holder" class="col-sm-3">
<canvas id="chart-gamma" width="200" height="200"/></canvas>
</div>
<script defer>
var gammaChartData = {
datasets: [
{
label: 'Red',
data: [{x: 15, y: 30}, {x: 35, y: 17}, {x: 55, y: 37}, {x: 72, y: 45},],
borderColor: "red",
errors: [10, 28, 30, 34],
},
]
}
var ctx_gamma = document.getElementById("chart-gamma").getContext("2d");
window.onload = function() {
var gamma_chart = new Chart(ctx_gamma, {
type: 'scatter',
data: gammaChartData,
plugins: [errorbarPlugin],
options: {showLines: true},
});
};
</script>
</body>
</html>
Edit: shortened snippet by removing formatting and default options
Here's what I came up with (snippet below)
The errorbars can either be fixed to the data or shown independantly, and animate in / are hidden / revealed with the data.
The plugin can add x or y axis errors, repesented as bars (with/without caps) or ovals / circles (filled or transparent).
"use strict";
var errorbarPlugin = {
afterDraw: function (chart) {
var type = chart.config.type;
var plugConfig = chart.config.options.errorbarPlugin;
if (plugConfig) {
if (plugConfig.showErrors) {
var showErrors = plugConfig.showErrors;
}
}
else
showErrors = true;
if (showErrors !== false) {
if (["line", "scatter"].includes(type)) {
errorbarPlugin.scatterErrorbars(chart);
}
else if (type == "bar") {
console.log("Bar charts not supported yet");
}
}
},
scatterErrorbars: function (chart) {
var ctx = chart.ctx;
var plugConfig = chart.config.options.errorbarPlugin;
chart.data.datasets.forEach(function (dataset, i) {
var ds = dataset;
var meta = chart.getDatasetMeta(i);
var showErrors;
(ds.showErrors === false) ? showErrors = false : showErrors = true;
var errWidth;
(ds.errWidth) ? errWidth = ds.errWidth : errWidth = 1;
var showCap;
(ds.showCap) ? showCap = ds.showCap : showCap = true;
var capLen;
(ds.capLen) ? capLen = ds.capLen : capLen = 3;
var errStyle;
(ds.errStyle) ? errStyle = ds.errStyle : errStyle = "T";
var errFillColor;
(ds.errFillColor) ? errFillColor = ds.errFillColor : errFillColor = "rgba(0,0,0,0)";
if (!meta.hidden && showErrors) {
meta.data.forEach(function (element, index) {
var x_point = element._model.x;
var y_point = element._model.y;
var errColor;
(ds.errColor) ? errColor = ds.errColor : errColor = element._view.borderColor;
var dataPoint = ds.data[index];
var yError;
var xError;
if (typeof (dataPoint) === "object" && 'r' in dataPoint) {
yError = dataPoint.r;
}
else if (ds.errors) {
yError = ds.errors[index];
}
else {
yError = null;
}
if (typeof (dataPoint) === "object" && dataPoint.e) {
xError = dataPoint.e;
}
else if (ds.xErrors) {
xError = ds.xErrors[index];
}
else {
xError = null;
}
var position = element.tooltipPosition();
if (errStyle == "circle") {
ctx.beginPath();
ctx.arc(position.x, position.y, yError, 0, 2 * Math.PI, false);
if (ds.hidden === true && meta.hidden === null) {
ctx.strokeStyle = "rgba(0,0,0,0)";
ctx.fillStyle = "rgba(0,0,0,0)";
}
else {
ctx.strokeStyle = errColor;
ctx.fillStyle = errFillColor;
}
console.log(meta.hidden);
ctx.fill();
ctx.stroke();
}
else if (errStyle == "oval" || errStyle == "ellipse") {
if (xError) {
var scaleFac = (xError) / yError;
}
else
scaleFac = 10 / yError;
ctx.beginPath();
ctx.save();
ctx.scale(scaleFac, 1);
ctx.arc(position.x / scaleFac, position.y, yError, 0, 2 * Math.PI, false);
ctx.restore();
if (ds.hidden === true && meta.hidden === null) {
ctx.strokeStyle = "rgba(0,0,0,0)";
}
else {
ctx.strokeStyle = errColor;
}
ctx.stroke();
}
else {
ctx.beginPath();
ctx.moveTo(position.x, position.y - yError);
ctx.lineTo(position.x, position.y + yError);
if (xError) {
ctx.moveTo(position.x - xError, position.y);
ctx.lineTo(position.x + xError, position.y);
}
if (ds.hidden === true && meta.hidden === null) {
ctx.strokeStyle = "rgba(0,0,0,0)";
}
else {
ctx.strokeStyle = errColor;
}
ctx.stroke();
if (showCap) {
ctx.beginPath();
ctx.moveTo(position.x - capLen, position.y - yError);
ctx.lineTo(position.x + capLen, position.y - yError);
ctx.moveTo(position.x - capLen, position.y + yError);
ctx.lineTo(position.x + capLen, position.y + yError);
if (xError) {
ctx.moveTo(position.x - xError, position.y - capLen);
ctx.lineTo(position.x - xError, position.y + capLen);
ctx.moveTo(position.x + xError, position.y - capLen);
ctx.lineTo(position.x + xError, position.y + capLen);
}
ctx.stroke();
}
}
});
}
});
}
};
<!DOCTYPE html>
<!--DOCTYPE html -->
<html>
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js'></script>
<body>
<div style = "position:relative;
width:60%;" >
<div id="canvas-holder" class="col-sm-6">
<canvas id="chart-gamma" width="500" height="500"/></canvas>
</div>
<div id="canvas-holderbf2" class="col-sm-6">
<canvas id="chart-humid" width="500" height="500"/></canvas>
</div>
<script defer>
Chart.defaults.global.legend.display = true
Chart.defaults.global.legend.position = 'right'
// Chart.defaults.global.legend.onHover = function(){}
// Chart.defaults.global.legend.onClick = function(){}
Chart.defaults.global.legend.labels.usePointStyle = true
Chart.defaults.global.legend.labels.fontsize = 12
Chart.defaults.global.legend.labels.padding = 10
var gammaChartData = {
datasets: [
{
label: 'Eu',
data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
borderColor: "red",
//fillColor: "pink",
errors: [15, 20, 30, 12, 10, 10],
xErrors: [3, 7, 16, 12, 12, 30, 10],
//hidden: true,
errColor: "blue",
errStyle: "circle",
errFillColor: "pink",
hidden: true,
errWidth: 2,
showCap: true,
capLen: 3,
showErrors: true,
},
{
label: 'Am',
data: [{x: 15, y: 85, r: 14}, {x: 25, y: 37, r: 8}, {x: 62, y: 135, r: 44},],
borderColor: "blue",
errColor: "red",
errStyle: "circle",
showErrors: true,
},
]
}
var options_gamma = {
animation: {
duration: 1000,
},
errorbarPlugin: {
showErrors: true,
marginsOfError: [100, 50, 10],
},
elements: {
line: { fill: false,
borderWidth: 1,
},
point: { radius: 0,
pointStyle: 'circle',
borderWidth: 1,
hitRadius: 18, //size if should hover
// hoverBorderWidth: 13,
hoverRadius: 10, //size when hovered
},
},
annotation: {
annotations: [{
id: 'h-line-01', // optional
type: 'line',
mode: 'horizontal',
scaleID: 'y-axis-0',
value: '125',
borderColor: 'red',
borderDash: [2, 2],
borderWidth: 2,
label: {
enabled: true,
backgroundColor: 'rgba(255,255,255,1)', // Background color of label, default below
//fontFamily: "sans-serif", // Font family of text, inherits from global
fontStyle: "normal", // Font style of text, default "bold"
fontSize: 12, // Font size of text, inherits from global
fontColor: "red",// Font color of text, default below
xPadding: 5,// Padding of label to add top/bottom, default below
yPadding: 5,// Radius of label rectangle, default below
cornerRadius: 10, // Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
position: "left", // Adjustment along x-axis (left-right) of label relative to above number (can be negative)
// For horizontal lines positioned left or right, negative values move the label toward the edge, and negative values toward the center.
xAdjust: 290, // Adjustment along y-axis (top-bottom) of label relative to above number (can be negative)
// For vertical lines positioned top or bottom, negative values move the label toward the edge, and negative values toward the center.
yAdjust: 0, // Whether the label is enabled and should be displayed
// Text to display in label - default is null
content: "Max"
},
onClick: function(e) { // Fires when the user clicks this annotation on the chart (be sure to enable the event in the events array below).
}
}],
},
responsive: true,
showLines: true,
hoverMode: 'single', // should always use single for a scatter chart
legend: {},
scales: {
yAxes: [{
display: true,
position: 'left',
id: 'y-axis-0',
ticks: {min: 0, //beginAtZero:true,
max: 200,
//display: true,
//fontColor: "black"
},
scaleLabel: {display: true, labelString: 'Number'},
gridLines: {color: "black",
//display: true,
drawOnChartArea: false,
zeroLineColor: "black",
//drawTicks: true,
},
}],
xAxes: [{
display: true,
type: 'linear',
id: 'x-axis-0',
position: 'bottom',
ticks: {min: 0,
max: 100,
//display: true,
//fontColor: "black",
},
scaleLabel: {display: true, labelString: 'Volume'},
gridLines: {color: "black",
zeroLineColor: "black",
drawOnChartArea: false,
},
}],
},
}
var ctx_gamma = document.getElementById("chart-gamma").getContext("2d");
var humidChartData = {
datasets: [
{
label: 'B errors',
data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
borderColor: "green",
errors: [15, 20, 30, 12, 10, 10],
xErrors: [3, 7, 16, 12, 12, 30, 10],
errStyle: "oval",
showLine: false,
errColor: "border",
//pointBackgroundColor: "white",
//pointBordercolor: "white",
backgroundColor: "rgba(0,0,0,0)",
hidden: true,
errWidth: 2,
showCap: true,
capLen: 3,
radius: 0,
showErrors: true,
},
{
label: 'B trend',
data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
borderColor: "green",
errors: [15, 20, 30, 12, 10, 10],
xErrors: [3, 7, 16, 12, 12, 30, 10],
pointStyle: "line",
showErrors: false,
radius: 0,
},
{
label: 'B data',
data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
borderColor: "green",
backgroundColor: "green",
errors: [15, 20, 30, 12, 10, 10],
xErrors: [3, 7, 16, 12, 12, 30, 10],
showErrors: false,
showLine: false,
},
{
label: '',
data: [],
borderColor: "rgba(0,0,0,0)",
backgroundColor: "rgba(0,0,0,0)",
},
{
label: 'C data',
data: [{x: 15, y: 85, r: 14}, {x: 25, y: 37, r: 8}, {x: 62, y: 135, r: 44},],
borderColor: "blue",
backgroundColor: "rgba(0,0,0,0)",
xErrors: [3, 7, 16, 12, 12, 30, 10],
showLine: true,
showErrors: true,
},
]
}
var options_humid = {
hoverMode: 'single',
elements: {
line: { fill: false,
borderWidth: 2,
},
point: { radius: 3,
pointStyle: 'circle',
borderWidth: 1,
hitRadius: 0,
// hoverBorderWidth: 13,
hoverRadius: 9,
},
},
responsive: true,
showLines: true,
hoverMode: 'single', // should always use single for a scatter chart
legend: {
labels: {
usePointStyle: true,
// generateLabels: function() { }
}
},
scales: {
yAxes: [{
display: true,
position: 'left',
id: 'y-axis-0',
ticks: {min: 0, //beginAtZero:true,
max: 300 },
scaleLabel: {display: true, labelString: 'Number'},
gridLines: {zeroLineColor: "black", },
}],
xAxes: [{
display: true,
type: 'linear',
id: 'x-axis-0',
position: 'bottom',
ticks: {min: 0,
max: 200 },
scaleLabel: {display: true, labelString: 'Month'},
gridLines: {zeroLineColor: "black", },
}],
},
}
var ctx_humid = document.getElementById("chart-humid").getContext("2d");
window.onload = function() {
var humidChart = new Chart(ctx_humid, {
type: 'line',
data: humidChartData,
plugins: [errorbarPlugin],
options: options_humid,
});
var gamma_chart = new Chart(ctx_gamma, {
type: 'scatter',
data: gammaChartData,
plugins: [errorbarPlugin],
options: options_gamma,
});
};
</script>
</div>
</body>
</html>
I had a similar issue with rendered text in
plugins:[{
afterDatasetsDraw: function(chart, options) {
var ctx = chart.ctx;
ctx.font = Chart.defaults.global.defaultFontStyle;
ctx.fillStyle = "#666666";
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
chart.data.datasets.forEach(function(dataset, i) {
var meta = chart.controller.getDatasetMeta(i);
meta.data.forEach(function (bar, index) {
ctx.fillText(Math.round(dataset.data[index]), bar._model.x, bar._model.y);
});
});
}
}]
To fix, inside the meta.data.forEach(function (bar, index) { ... } loop, simply use:
var position = bar.tooltipPosition();
ctx.fillText(Math.round(dataset.data[index]), position.x, position.y);
(This is in #Yobmod's post, but it seems the key is to use the bar.tooltipPosition() location for the location of whatever is rendered.)
Searched a lot about this but din't got any answer on this based on chart.js. I had already asked this question here in which I was using highchart.js and got the solution but now I am using chart.js library and trying to find the solution.
Below is my code I have tried. I need to find the intersection point between these 2 line graphs combo. See the Graph Image .
var config = {
type: 'bar',
data: {
labels: ["Year 0", "Year 1", "Year 2", "Year 3", "Year 4", "Year 5", "Year 6"],
datasets: [{
type: 'line',
label: 'Cost',
data: [150, 15, 25, 14, 10, 7],
borderColor: '#E35500',
fill: false,
lineTension: 0,
borderJoinStyle: 'miter',
}, {
type: 'line',
label: 'Cash Flow',
data: [20, 180, 170, 220, 160, 190],
borderColor: '#FFC000',
fill: false,
lineTension: 0,
borderJoinStyle: 'miter',
xAxes: [{
barPercentage: 0.4
}]
},
{
type: 'line',
label: 'Accumulative Flow',
data: [0, -10, 20, 30, 40, 50],
borderColor: 'red',
fill: false,
lineTension: 0,
borderJoinStyle: 'miter',
xAxes: [{
barPercentage: 0.4
}]
}, {
type: 'bar',
label: 'Benifit(One time)',
backgroundColor: "#005998",
data: [40, 50, 60, 80, 50, 60],
}, {
type: 'bar',
label: 'Benifit(Recurring)',
backgroundColor: "#0FAAFF",
data: [120, 150, 150, 180, 120, 140],
}
]
},
options: {
title: {
display: true,
text: 'Custom Chart Title'
},
scales: {
xAxes: [{
time: {
displayFormats: {
quarter: ' YYYY'
}
},
stacked: true,
beginAtZero: true,
barPercentage: 0.3,
id: 'x-axis-label',
position: 'bottom',
scaleStartValue: 20,
gridLines: {
display: false
},
}],
yAxes: [{
stacked: true,
id: 'y-axis-label',
ticks: {
max: 300,
min: -50,
stepSize: 50,
},
position: 'left',
gridLines: {
display: false
},
}]
},
legend: {
position: 'right'
},
maintainAspectRatio: false,
scaleBeginAtZero: true
}
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
.GraphContain {
max-height: 500px;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.0/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<div class="GraphContain">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
Anyone having any idea?? need a genius on this!!!
Here we are )))
https://jsfiddle.net/Falseclock/5nbecn0z/
If you need to show intersections with X axis, then just imitate line with Y=0
https://jsfiddle.net/Falseclock/8g0ucdb1/
var ORDER_STATS = {
"2016" : [10, 181, 194, -56, 130, 181, 179, 189, 30, 60, 193, 154],
"2015" : [124, -50, 152, 187, 10, 164, 129, -16, 115, 119, 129, 171],
"2014" : [-90, 80, 30, 59, 100, -30, 60, 116, 191, 181, -60, 106]
};
var colors = ['206,191,26','119,206,26','26,200,206','236,124,98','206,26,140','26,77,206'];
// Definning X
var ordersChartData = {
labels : ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
datasets : []
}
Object.keys(ORDER_STATS).forEach(function (key) {
color = colors.shift();
ordersChartData.datasets.push(
{
label: key,
lineTension: 0,
type: 'line',
backgroundColor: "rgba("+color+",0.1)",
borderColor: "rgba("+color+",1)",
borderWidth: 2,
pointBackgroundColor : "rgba("+color+",1)",
pointBorderColor: "#fff",
pointBorderWidth: 1,
pointRadius: 4,
pointHoverBackgroundColor : "#fff",
pointHoverBorderColor: "rgba("+color+",1)",
pointHoverBorderWidth: 1,
data : ORDER_STATS[key]
}
);
});
var ctx = document.getElementById("myChart").getContext("2d");
Chart.defaults.global.defaultFontColor = 'grey';
Chart.defaults.global.defaultFontFamily = "Tahoma";
Chart.defaults.global.defaultFontSize = 11;
Chart.defaults.global.defaultFontStyle = 'normal';
var myChart = new Chart(ctx, {
type: 'line',
data: ordersChartData,
defaultFontSize: 11,
options: {
responsive: true,
title: {
display: true,
text: 'Intersection realization',
fontColor: "#444",
fontFamily: 'Tahoma',
padding: 0
},
legend: {
display: true,
labels: {
fontColor: 'grey',
usePointStyle: true
}
},
tooltips: {
mode: "index",
intersect: true,
position: 'nearest',
bodySpacing: 4
}
}
});
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = Y.top + ((Y.bottom - Y.top) / (Y.ticks.length -1) * Y.zeroLineIndex);
zeroPointX = Y.right;
yScale = (Y.bottom - Y.top) / (Y.end - Y.start);
xScale = (X.right - X.left) / (X.ticks.length - 1);
var intersects = findIntersects(ORDER_STATS['2015'], ORDER_STATS['2014'] );
var context = chartInstance.chart.ctx;
intersects.forEach(function (result, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result.x * xScale) + zeroPointX, (Y.end - Y.start) - (result.y * yScale) - ((Y.end - Y.start) - zeroPointY), 3, 0, 2 * Math.PI, true);
context.fill();
});
}
});
function findIntersects(line1, line2)
{
var intersects = [];
line1.forEach(function(val,idx) {
var line1StartX = idx;
var line1StartY = line1[idx];
var line1EndX = idx + 1;
var line1EndY = line1[idx + 1];
var line2StartX = idx;
var line2StartY = line2[idx];
var line2EndX = idx + 1;
var line2EndY = line2[idx+1];
result = checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY);
if (result.onLine1 && result.onLine2) {
intersects.push(result);
}
});
return intersects;
}
function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
var denominator, a, b, numerator1, numerator2, result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
if (denominator == 0) {
return result;
}
a = line1StartY - line2StartY;
b = line1StartX - line2StartX;
numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
a = numerator1 / denominator;
b = numerator2 / denominator;
// if we cast these lines infinitely in both directions, they intersect here:
result.x = line1StartX + (a * (line1EndX - line1StartX));
result.y = line1StartY + (a * (line1EndY - line1StartY));
/*
// it is worth noting that this should be the same as:
x = line2StartX + (b * (line2EndX - line2StartX));
y = line2StartX + (b * (line2EndY - line2StartY));
*/
// if line1 is a segment and line2 is infinite, they intersect if:
if (a > 0 && a < 1) {
result.onLine1 = true;
}
// if line2 is a segment and line1 is infinite, they intersect if:
if (b > 0 && b < 1) {
result.onLine2 = true;
}
// if line1 and line2 are segments, they intersect if both of the above are true
return result;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="myChart" width="650" height="241" style="display: block; width: 650px; height: 241px;"></canvas>
I'm new to chart.js and I'm having trouble shading between 2 lines on a line graph. Below is an example of what id like to achieve:
This is an example of what im after
but the base functionality in chart.js version 2 seems to only shade between a line and 0 on the y-axis.
Here is the code for the graph I have so far. I would like to shade between the GTUpper and GTLower lines as these describe a range.
<html>
<div>
<canvas id="myChart"></canvas>
</div>
</html>
<script>
function GenGraph(y) {
// function to generate the graph with chart.js
$(document).ready(function(){
var roomCap = 220
var prediction = [62, 65, 135, 145, 140, 120, 135, 189, 180, 175, 100, 25]
var gndTruthUpper = [75, 100, 150, 175, 150, 150, 175, 200, 175, 150, 125, 100]
var gndTruthLower = [50, 50, 75, 50, 25, 50, 75, 100, 125, 150, 125, 100, 75]
var data = {
labels: ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"],
datasets: [{
label: 'prediction',
fill: false,
pointRadius: 0,
borderColor: 'blue',
data: prediction
},{
label: 'GTUpper',
fill: true,
pointRadius: 0,
borderDash: [10, 10],
borderColor: 'black',
data: gndTruthUpper
},{
label: 'GTLower',
fill: false,
pointRadius: 0,
borderDash: [10, 10],
borderColor: 'black',
data: gndTruthLower
}]
};
var options = {
scales: {
yAxes: [{
ticks: {
min: 0,
max: roomCap
}
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});
});
};
</script>
My issue is that any similar stackoverflow posts refer to v1 and the syntax seems to have changed from v1 to v2. I'm a bit lost as to how I can extend the base functionality in this way myself. Any help would be greatly appreciated.
This should do the trick.
And Here is a fiddle of a plugin that will fill between any two datasets.
https://jsfiddle.net/ke5n5LnL/26/
Code:
<html>
<div>
<canvas id="myChart"></canvas>
</div>
</html>
<script>
var fillBetweenLinesPlugin = {
afterDatasetsDraw: function (chart) {
var ctx = chart.chart.ctx;
var xaxis = chart.scales['x-axis-0'];
var yaxis = chart.scales['y-axis-0'];
var datasets = chart.data.datasets;
ctx.save();
for (var d = 0; d < datasets.length; d++) {
var dataset = datasets[d];
if (dataset.fillBetweenSet == undefined) {
continue;
}
// get meta for both data sets
var meta1 = chart.getDatasetMeta(d);
var meta2 = chart.getDatasetMeta(dataset.fillBetweenSet);
ctx.beginPath();
// vars for tracing
var curr, prev;
// trace set1 line
for (var i = 0; i < meta1.data.length; i++) {
curr = meta1.data[i];
if (i === 0) {
ctx.moveTo(curr._view.x, curr._view.y);
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
if (curr._view.steppedLine === true) {
ctx.lineTo(curr._view.x, prev._view.y);
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
if (curr._view.tension === 0) {
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
ctx.bezierCurveTo(
prev._view.controlPointNextX,
prev._view.controlPointNextY,
curr._view.controlPointPreviousX,
curr._view.controlPointPreviousY,
curr._view.x,
curr._view.y
);
prev = curr;
}
// connect set1 to set2 then BACKWORDS trace set2 line
for (var i = meta2.data.length - 1; i >= 0; i--) {
curr = meta2.data[i];
if (i === meta2.data.length - 1) {
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
if (curr._view.steppedLine === true) {
ctx.lineTo(prev._view.x, curr._view.y);
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
if (curr._view.tension === 0) {
ctx.lineTo(curr._view.x, curr._view.y);
prev = curr;
continue;
}
// reverse bezier
ctx.bezierCurveTo(
prev._view.controlPointPreviousX,
prev._view.controlPointPreviousY,
curr._view.controlPointNextX,
curr._view.controlPointNextY,
curr._view.x,
curr._view.y
);
prev = curr;
}
ctx.closePath();
ctx.fillStyle = dataset.fillBetweenColor || "rgba(0,0,0,0.1)";
ctx.fill();
}
} // end afterDatasetsDraw
}; // end fillBetweenLinesPlugin
Chart.pluginService.register(fillBetweenLinesPlugin);
function GenGraph(y) {
// function to generate the graph with chart.js
$(document).ready(function(){
var roomCap = 220
var prediction = [62, 65, 135, 145, 140, 120, 135, 189, 180, 175, 100, 25]
var gndTruthUpper = [75, 100, 150, 175, 150, 150, 175, 200, 175, 150, 125, 100]
var gndTruthLower = [50, 50, 75, 50, 25, 50, 75, 100, 125, 150, 125, 100, 75]
var data = {
labels: ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"],
datasets: [{
label: 'prediction',
fill: false,
pointRadius: 0,
borderColor: 'blue',
data: prediction
},{
label: 'GTUpper',
fill: false,
pointRadius: 0,
borderDash: [10, 10],
borderColor: 'black',
data: gndTruthUpper,
fillBetweenSet: 2,
fillBetweenColor: 'rgba(0,0,0,0.1)'
},{
label: 'GTLower',
fill: false,
pointRadius: 0,
borderDash: [10, 10],
borderColor: 'black',
data: gndTruthLower
}]
};
var options = {
scales: {
yAxes: [{
ticks: {
min: 0,
max: roomCap
}
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});
});
};
GenGraph();
</script>