ChartJs - Angle line and ticks 'on top' - chart.js

I have a chart as shown below. I have two expectation, firstly as you can see the tick number (1,2,3,4,5) hidden behind the background color of dump datasets (I need that background colors, that's why I am using fake datasets). I need to show them like using z-index or something. I wrote tick numbers in red color for sample.
Secondly I need the show angle line on top also.
var test2 = [2, 2, 2, 2, 2];
var test3 = [3, 3, 3, 3, 3];
var test4 = [4, 4, 4, 4, 4];
var test5 = [5, 5, 5, 5, 5];
new Chart(document.getElementById("myChart"), {
type: 'radar',
data: {
labels: sectionDescriptions,
datasets: [
{
label: "2050",
fill: true,
borderColor: "rgba(0,0,0,1)",
borderWidth: "4",
pointRadius: 3,
pointBorderWidth: 3,
pointBackgroundColor: "cornflowerblue",
pointBorderColor: "rgba(0,0,200,0.6)",
pointHoverRadius: 10,
data: sectionPoints
},
{
radius: 0,
fill: true,
backgroundColor: "rgba(240,0,0,1)",
data: test2
},
{
radius: 0,
fill: true,
backgroundColor: "rgba(255,190,4,1)",
data: test3
},
{
radius: 0,
fill: true,
backgroundColor: "rgba(255,250,5,1)",
data: test4
},
{
radius: 0,
fill: true,
backgroundColor: "rgba(0,180,233,1)",
data: test5
}
]
},
options: {
title: {
display: true,
text: 'Chart'
},
scale: {
gridLine: {
color : "white"
},
ticks: {
beginAtZero: true,
min: 0,
max: 5,
stepSize: 1,
fontColor: 'black',
backDropColor: 'black'
},
pointLabels: {
fontSize: 13,
//fontStyle: 'bold'
}
},
legend: {
display: false
}
}
});

Related

ChartJs line chart - display permanent icon above some data points with text on hover

I have a chart i need to draw that, in addition to the data points, i want an icon permanently above (some) data points with a custom text string.
I don't need the default value popover as i can make do with a custom legend, but i need to add an icon above one or two points, and on hover of the icon, display a popover. i need to build the popover text string from non-chart related data.
The custom data labels don't appear to be flexible enough to allow different icons/popovers on different data points, i may be wrong though.
Another possibility is chartjs-plugin-datalabels, but I'm not sure about that.
In case your chart (canvas) is of fixed size, you can easily solve this problem by adding an additional dataset that specifies nothing but the icons to be shown in the chart.
{
data: data2.map((v, i) => imageIndexes.includes(i) ? v + 1.2 : null),
fill: false,
pointStyle: icon,
pointRadius: 22,
pointHoverRadius: 22
}
Given the data array data2 and the array imageIndexes, the data of the icons dataset can be built using Array.map. Note that the values - where any - are derived from corresponding values in data2 but slightly increased to make the images appear on top of them.
data2.map((v, i) => imageIndexes.includes(i) ? v + 1.2 : null)
Further you'll need to define a tooltips object inside the chart options in order to style the popup and to make sure, tooltips are only displayed when the mouse hovers over the icons.
tooltips: {
filter: tooltipItem => tooltipItem.datasetIndex == 2,
titleFontSize: 16,
titleAlign: 'center',
callbacks: {
title: (tooltipItem) => tooltipItem.length == 0 ? null : tooltipText,
label: () => null
}
},
Please have a look at the runnable code snipped below.
const labels = ['A', 'B', 'C', 'D', 'E', 'F'];
const alerts = ['B', 'D'];
const data1 = [0, 2, 1, 3, 2, 1];
const data2 = [1, 3, 3, 4, 3, 2];
const imageIndexes = [1, 3];
const tooltipText = 'Efficiency of Standard Curve\nnot opimal';
var icon = new Image();
icon.src = 'https://i.stack.imgur.com/YvlWY.png';
const chart = new Chart(document.getElementById("myChart"), {
type: "line",
data: {
labels: labels,
datasets: [{
data: data1,
fill: false,
backgroundColor: 'blue',
borderColor: 'blue',
lineTension: 0,
pointRadius: 5,
pointHoverRadius: 5,
pointBorderWidth: 3,
pointHoverBorderWidth: 3,
pointBorderColor: 'white',
pointHoverBorderColor: 'white'
},
{
data: data2,
fill: false,
showLine: false,
backgroundColor: 'orange',
pointRadius: 4,
pointHoverRadius: 4
},
{
data: data2.map((v, i) => imageIndexes.includes(i) ? v + 1.2 : null),
fill: false,
pointStyle: icon,
pointRadius: 22,
pointHoverRadius: 22
}
]
},
options: {
responsive: false,
title: {
display: false
},
legend: {
display: false
},
tooltips: {
filter: tooltipItem => tooltipItem.datasetIndex == 2,
titleFontSize: 16,
titleAlign: 'center',
callbacks: {
title: (tooltipItem) => tooltipItem.length == 0 ? null : tooltipText,
label: () => null
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 6,
stepSize: 1
}
}],
xAxes: [{
gridLines: {
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" style="width: 500px; height: 200px"></canvas>

Custom scatter chart extended from scatter chart becomes line chart

I'm unable to find an explanation so as why my customized scatter charts display a line chart instead.
I've created this custom chart by extending it from scatter using the basic :
Chart.defaults.scatterCustom = Chart.defaults.scatter;
Chart.controllers.scatterCustom = Chart.controllers.scatter.extend({ ...
Following is the entire code i'm using and a fiddle to illustrate my problem.
We can see the 3 differents charts, 2 are default scatter and one is customScatter.
The draw function was added later, thinking it would bring back the normal behaviour but it doesn't change anything.
Any help greatly appreciated!
tidechart = {
tideChart: "",
initTidechart: function() {
Chart.defaults.scatterCustom = Chart.defaults.scatter;
Chart.controllers.scatterCustom = Chart.controllers.scatter.extend({
draw: function(ease) {
Chart.controllers.scatter.prototype.draw.call(this, ease);
},
fireSliderEvent: function(point, canvas, boundingRect) {
var mouseX = Math.round((boundingRect.left + point._view.x) / (boundingRect.right - boundingRect.left) * canvas.width / this.chart.chart.currentDevicePixelRatio);
var mouseY = Math.round((boundingRect.top + point._view.y) / (boundingRect.bottom - boundingRect.top) * canvas.height / this.chart.chart.currentDevicePixelRatio);
var oEvent = document.createEvent('MouseEvents');
oEvent.initMouseEvent('click', true, true, document.defaultView,
0, mouseX, mouseY, mouseX, mouseY,
false, false, false, false, 0, canvas);
canvas.dispatchEvent(oEvent);
},
highlightPointsByIndex: function(datasetIndex, index) {
var canvas = this.chart.canvas;
var boundingRect = canvas.getBoundingClientRect();
var points = this.chart.getDatasetMeta(datasetIndex).data;
this.fireSliderEvent(points[index], canvas, boundingRect);
},
highlightPointsByValue: function(points, value) {
var dataPoints = this._data;
for (var index = 0; index < dataPoints.length; ++index) {
if (dataPoints[index].toString() === value.toString()) {
this.highlightPointsByIndex(points, index);
break;
}
}
}
});
tideChartOptions = {
maintainAspectRatio: false,
legend: {
display: true
},
tooltips: {
backgroundColor: '#f5f5f5',
titleFontColor: '#333',
bodyFontColor: '#666',
bodySpacing: 4,
xPadding: 12,
mode: "nearest",
intersect: 0,
position: "nearest",
filter: function(tooltipItem) {
return tooltipItem.datasetIndex != 0;
}
},
responsive: true,
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Tide level'
},
gridLines: {
drawBorder: false,
color: 'rgba(29,140,248,0.0)',
zeroLineColor: "transparent",
},
ticks: {
suggestedMin: 0,
suggestedMax: 10,
padding: 20,
fontColor: "#9a9a9a"
},
afterTickToLabelConversion: function(q) {
for (var tick in q.ticks) {
q.ticks[tick] += ' m';
}
},
}],
xAxes: [{
display: true,
type: 'time',
time: {
parser: 'YYYY-MM-DD HH:mm:ss',
tooltipFormat: 'll HH:mm',
unit: 'day',
unitStepSize: 1,
displayFormats: {
'month': 'MMM DD'
}
},
gridLines: {
drawBorder: false,
color: 'rgba(225,78,202,0.1)',
zeroLineColor: "transparent",
},
ticks: {
padding: 20,
fontColor: "#9a9a9a"
}
}]
}
};
var data = {
labels: ['01/01/2020','01/02/2020','01/03/2020','01/04/2020'],
datasets: [
{
label: 'Camera 1',
type: 'scatter',
fill: false,
backgroundColor: '#2DAAE1',
borderColor: '#2DAAE1',
borderWidth: 0,
pointBackgroundColor: '#2DAAE1',
pointHoverBackgroundColor: '#2DAAE1',
pointBorderWidth: 0,
pointHoverRadius: 5,
pointRadius: 2,
data: [1,5,6,7]
},
{
fill: false,
type: 'scatter',
label: 'Camera 2',
backgroundColor: '#FFC329',
borderColor: '#FFC329',
borderWidth: 2,
borderDash: [],
borderDashOffset: 0.0,
pointBackgroundColor: '#FFC329',
pointHoverBackgroundColor: '#FFC329',
pointBorderWidth: 0,
pointHoverRadius: 5,
pointRadius: 2,
data: [3,2,8,1]
},
{
fill: false,
type: 'scatterCustom',
label: 'Camera 3',
backgroundColor: '#FF5229',
borderColor: '#FF5229',
borderWidth: 2,
borderDash: [],
borderDashOffset: 0.0,
pointBackgroundColor: '#FF5229',
pointHoverBackgroundColor: '#FF5229',
pointBorderWidth: 0,
pointHoverRadius: 5,
pointRadius: 2,
data: [8,4,5,3]
}
]
};
//tidechart.tideChart = new Chart(ctx, config);
tidechart.tideChart = new Chart($("#tidechart"), {
data: data,
options: tideChartOptions
});
}
}
tidechart.initTidechart();
This solves it
Chart.defaults.global.datasets.scatterCustom = Chart.defaults.global.datasets.scatter;

How can I datalabels and Sum display in the same time on a stacked bar

I try to have in the same time datalabels and the sum display in my stacked bar chart.
I use chartjs-plugin-datalabels.js to display datalabels or sum.
Above just with datalabels:
Above just with sum :
I would like to have both in the same chart.
I use the follow tricks to sum display :
<script>
const totalizer = {
id: 'totalizer',
beforeUpdate: chart => {
let totals = {}
let utmost = 0
chart.data.datasets.forEach((dataset, datasetIndex) => {
if (chart.isDatasetVisible(datasetIndex)) {
utmost = datasetIndex
dataset.data.forEach((value, index) => {
totals[index] = (totals[index] || 0) + value
})
}
})
chart.$totalizer = {
totals: totals,
utmost: utmost
}
}
}
new Chart('chart', {
type: 'bar',
data: {
labels: ["Note par thème du répondant", "Note par thème sectorielle", "Note par thème générale"],
datasets: [{
label: 'Thème 1',
data: [12, 19, 3],
backgroundColor: 'rgba(255, 206, 86)'
},
{
label: 'Thème 2',
data: [3,8,1],
backgroundColor: 'rgba(54, 162, 235)'
},
{
label: 'Thème 3',
data: [ 5, 2, 3],
backgroundColor: 'rgba(255, 255, 132)'
},{
label: 'Thème 4',
data: [ 5, 2, 3],
backgroundColor: 'rgba(255, 99, 255)'
},{
label: 'Thème 5',
data: [ 5, 2, 3],
backgroundColor: 'rgba(100, 250, 132)'
},{
label: 'Thème 6',
data: [ 5, 2, 3],
backgroundColor: 'rgba(89, 99, 132)'
},{
label: 'Thème 7',
data: [ 5, 2, 3],
backgroundColor: 'rgba(255, 0, 132)'
},{
label: 'Thème 8',
data: [ 5, 2, 3],
backgroundColor: 'rgba(0, 150, 255)'
},{
label: 'Thème 9',
data: [ 5, 2, 3],
backgroundColor: 'rgba(100, 100, 100)'
},{
label: 'Thème 10',
data: [ 10, 4, 7],
backgroundColor: 'rgba(255, 255, 0)'
}]
},
options: {
title :{
display :true,
text : "Mon titre"
},
scales: {
yAxes: [{
stacked: true,
ticks: {
beginAtZero: true,
suggestedMax: 100
}
}],
xAxes: [{
stacked: true
}]
},
plugins: {
datalabels: {
formatter: (value, ctx) => {
const total = ctx.chart.$totalizer.totals[ctx.dataIndex]
return total.toLocaleString('fr-FR', {})
},
align: 'end',
anchor: 'end',
display: function(ctx) {
return ctx.datasetIndex === ctx.chart.$totalizer.utmost
}
}
}
},
plugins: [totalizer]
});
</script>
I would appreciate any help. Thanks!
OK, I found a way to show Sum and datalabels in the same time.
I add new dataset to represent "Total data" with a backgroundColor transparent and I add this following code in my dataset Total:
datalabels: {
backgroundColor: function (context) {
return 'rgba(244,50,49)';
},
formatter: (value, ctx) => {
const total = ctx.chart.$totalizer.totals[ctx.dataIndex];
return total.toLocaleString('fr-FR', {})
},
align: 'end',
anchor: 'end',
display: function (ctx) {
return ctx.datasetIndex === ctx.chart.$totalizer.utmost
}
Full code bellow :
<script>
const totalizer = {
id: 'totalizer',
beforeUpdate: chart => {
let totals = {}
let utmost = 0
chart.data.datasets.forEach((dataset, datasetIndex) => {
if (chart.isDatasetVisible(datasetIndex)) {
utmost = datasetIndex
dataset.data.forEach((value, index) => {
totals[index] = (totals[index] || 0) + value
})
}
})
chart.$totalizer = {
totals: totals,
utmost: utmost
}
}
}
new Chart('chart', {
type: 'bar',
data: {
labels: ["Note par thème du répondant", "Note par thème sectorielle", "Note par thème générale"],
datasets: [{
label: 'Thème 1',
data: [10, 10, 10],
backgroundColor: 'rgba(68,114,196,0.6)'
}, {
label: 'Thème 2',
data: [13, 8, 11],
backgroundColor: 'rgba(237,125,49,0.6)'
}, {
label: 'Thème 3',
data: [5, 12, 13],
backgroundColor: 'rgba(165,165,165,0.6)'
}, {
label: 'Thème 4',
data: [9, 12, 13],
backgroundColor: 'rgba(255,192,0,0.6)'
}, {
label: 'Thème 5',
data: [5, 12, 9],
backgroundColor: 'rgba(91,155,213,0.6)'
}, {
label: 'Thème 6',
data: [9, 12, 5],
backgroundColor: 'rgba(112,173,71,0.6)'
}, {
label: 'Thème 7',
data: [5, 12, 10],
backgroundColor: 'rgba(38,68,120,0.6)'
}, {
label: 'Thème 8',
data: [15, 12, 10],
backgroundColor: 'rgba(99,99,99,0.6)'
}, {
label: 'Thème 9',
data: [15, 12, 11],
backgroundColor: 'rgba(37,94,145,0.6)'
}, {
label: 'Thème 10',
data: [10, 4, 7],
backgroundColor: 'rgba(24,91,62,0.6)'
}, {
label: 'Total',
data: [0, 0, 0],
backgroundColor: 'rgba(24,91,62,0)',
datalabels: {
backgroundColor: function (context) {
return 'rgba(244,50,49)';
},
formatter: (value, ctx) => {
const total = ctx.chart.$totalizer.totals[ctx.dataIndex];
return total.toLocaleString('fr-FR', {})
},
align: 'end',
anchor: 'end',
display: function (ctx) {
return ctx.datasetIndex === ctx.chart.$totalizer.utmost
}
}
}
]
},
options: {
title: {
text: "Mon titre",
display: true
},
scales: {
yAxes: [{
stacked: true,
ticks: {
beginAtZero: true,
suggestedMax: 100
}
}
],
xAxes: [{
stacked: true
}
]
},
plugins: {
datalabels: {
color: 'rgba(255,255,255)',
display: function (context) {
return context.chart.isDatasetVisible(context.datasetIndex);
},
backgroundColor: function (context) {
return context.dataset.backgroundColor;
},
borderRadius: 4,
font: {
weight: 'bold'
}
}
}
},
plugins: [totalizer]
});
</script>
Great! I just simplified solution without totilizer:
datasets: {
label: 'Total',
data: [120, 120, 120, 120, 120, 120, 120, 120, 120, 120],
backgroundColor: 'rgba(24,91,62,0)',
datalabels: {
backgroundColor: () => 'white',
formatter: (value, ctx) => {
let total = 0;
let index = ctx.dataIndex;
data.datasets.map((d, i) => {
if (i === 2) return;
total += d.data[index];
});
return total;
},
align: 'end',
anchor: 'end',
padding: 0,
},
},

Chartjs: Custom text on y axis at certain level

Is there any way to add a custom text to a certain level of the y axis of a chart created with Chartjs?
Something like this:
I've checked thick configuration options, but couldn't find anything that would result this kind of annotation.
Here is an example of what you can do using annotation plugin and custom drawing on canvas:
var data_set = [{x: 1, y: 12}, {x: 2, y: 3}, {x: 3, y: 2}, {x: 4, y: 1}, {x: 5, y: 8}, {x: 6, y: 8}, {x: 7, y: 2}, {x: 8, y: 2}, {x: 9, y: 3}, {x: 10, y: 5}, {x: 11, y: 11}, {x: 12, y: 1}];
var labels = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
var lines = [], id = 0;
var linesOn = false;
var data = {
labels: labels,
datasets: [{
label: "My First dataset",
backgroundColor: "rgba(5,215,2,0.2)",
borderColor: "rgba(5,215,2,1)",
borderWidth: 2,
hoverBackgroundColor: "rgba(5,215,2,0.4)",
hoverBorderColor: "rgba(5,215,2,1)",
data: data_set,
}]
};
addLine(7);
var option = {
legend: false,
title: {
display: true,
},
annotation: {
drawTime: "afterDraw",
annotations: lines
},
scales: {
xAxes: [{
id: 'x-axis',
type: 'linear',
position: 'bottom',
ticks: {
max: 12,
min: 1,
stepSize: 1,
callback: function(value, index, values) {
return data.labels[index];
}
}
}],
yAxes: [{
id: 'y-axis',
type: 'linear',
}],
},
animation: {
duration: 1,
onComplete: function () {
var ctx = this.chart.ctx;
console.log("onComplete", this,Chart.defaults.global)
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
var line = this.chart.annotation.elements.line1,
x = line._model.x1-15,
y = line._model.y1+5;
ctx.fillStyle = line.options.label.fontColor;
ctx.fillText("Y",x,y);
}
}
};
var myLineChart = Chart.Line('myChart', {
data: data,
options: option
});
function addLine(value) {
id++;
var ln = {
id: "line" + id,
type: "line",
mode: "horizontal",
scaleID: "y-axis",
value: value,
borderWidth: 2,
borderColor: "red",
label: {
enabled: false,
fontColor: "red",
}
};
lines.push(ln);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/0.5.7/chartjs-plugin-annotation.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
In the example I disabled annotation label showing and added a custom label on yAxis (see animation.onComplete() handler).

how to set chart.js grid color for line chart

I want to change the grids color of my line chart using options field but I have no idea from where to begin.
I First tried to change the canvas backgroud colors using gradient but the results weren't good.
canvas{
background:linear-gradient(top, #ea1a07 0%, #f4b841 75%,#f2dd43 100%);
}
However, I didn't get what I want because as you see in the above image, not only the grids were colored but also the x values, y labels and the chart legend were colored too.
Picture of the result I'm getting with this css code
Example of what I want to get
my chart.js options are
options = {
scales: {
xAxes: [{
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 1
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
max: 100,
min: 0,
stepSize: 10
},
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 0.5
}
}]
},
responsive: true
};
So, is there a way to set only the grid's background to 3 different colors (with gradient or not)?
NB: I'm using chart.js with angular 2 (ng2-charts)
The easiest way to do this is to use the chartjs-plugin-annotation plugin and configure 3 box annotations bound to your Y axis (each box would have a different color).
Here is an example below (and you can see it in action with this codepen).
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Points',
data: [
{x: 0, y: 2},
{x: 1, y: 3},
{x: 2, y: 2},
{x: 1.02, y: 0.4},
{x: 0, y: -1}
],
backgroundColor: 'rgba(123, 83, 252, 0.8)',
borderColor: 'rgba(33, 232, 234, 1)',
borderWidth: 1,
fill: false,
}],
},
options: {
title: {
display: true,
text: 'Chart.js - Gridline Background',
},
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
ticks: {
min: -1,
max: 8,
stepSize: 1,
fixedStepSize: 1,
},
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 1
}
}],
yAxes: [{
afterUpdate: function(scaleInstance) {
console.dir(scaleInstance);
},
ticks: {
min: -2,
max: 4,
stepSize: 1,
fixedStepSize: 1,
},
gridLines: {
color: 'rgba(171,171,171,1)',
lineWidth: 0.5
}
}]
},
annotation: {
annotations: [{
type: 'box',
yScaleID: 'y-axis-0',
yMin: 1,
yMax: 4,
borderColor: 'rgba(255, 51, 51, 0.25)',
borderWidth: 2,
backgroundColor: 'rgba(255, 51, 51, 0.25)',
}, {
type: 'box',
yScaleID: 'y-axis-0',
yMin: -1,
yMax: 1,
borderColor: 'rgba(255, 255, 0, 0.25)',
borderWidth: 1,
backgroundColor: 'rgba(255, 255, 0, 0.25)',
}, {
type: 'box',
yScaleID: 'y-axis-0',
yMin: -2,
yMax: -1,
borderColor: 'rgba(0, 204, 0, 0.25)',
borderWidth: 1,
backgroundColor: 'rgba(0, 204, 0, 0.25)',
}],
}
}
});
It's important to note that the annotations are painted on top of the chart, so your annotation color needs to contain some transparency to see the stuff behind it.
There is no problem using this plugin with ng2-charts. Just add the source in a <script> in your app's html file and add the annotation property into your options object. Here is an Angular2 / ng2-charts example demonstrating how to use the annotations plugin.