1👍
✅
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>
1👍
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.)
Source:stackexchange.com