I'm using Chart.js for line charts and I have the legend as below.
The problem is that the legend only has an outline color, I want the legend box to have the whole thing colored. I haven't found anything in the documentation to see why mine only has the border. I'm at a bit of a loss, here's an example of my setup:
var LinuxDistributionsCombined = document.getElementById('LinuxDistributionsCombined');
var myChart = new Chart.Line(LinuxDistributionsCombined, {
type: 'line',
data: {
labels: ['Jul-2016', 'Sep-2016', 'Oct-2016', 'Dec-2016', 'Jan-2017', 'Feb-2017', 'Mar-2017', 'Apr-2017', 'May-2017', 'Jun-2017', 'Jul-2017', 'Aug-2017', 'Sep-2017', 'Oct-2017'],
datasets: [{
label: 'Ubuntu-based',
fill: true,
data: [0, 0, 0, 0, 51.37, 51.04, 50.64, 50.29, 49.6, 48.32, 47.95, 47.03, 46.42, 46.21],
borderColor: '#a6cee3',
borderWidth: 1
}, {
label: 'Arch-based',
fill: true,
data: [0, 0, 0, 0, 28.52, 28.53, 28.75, 29.02, 29.16, 30.42, 30.65, 31.29, 31.53, 31.93],
borderColor: '#1f78b4',
borderWidth: 1
}, {
label: 'Solus',
fill: true,
data: [0, 0, 0, 0, 0.42, 0.45, 0.61, 0.64, 0.92, 1.12, 1.08, 1.21, 1.23, 1.46],
borderColor: '#6a3d9a',
borderWidth: 1
}]
},
options: {
legend: {
display: true
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Percentage of users'
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var label = data.datasets[tooltipItem.datasetIndex].label;
return label + ' ' + value + '%';
}
},
},
}
});
You have to set the backgroundColor property for each of your datasets as well, as that is correspondent to legend box's fill color.
...
datasets: [{
label: 'Ubuntu-based',
fill: true,
data: [0, 0, 0, 0, 51.37, 51.04, 50.64, 50.29, 49.6, 48.32, 47.95, 47.03, 46.42, 46.21],
backgroundColor: '#a6cee3',
borderColor: '#a6cee3',
borderWidth: 1
}, {
label: 'Arch-based',
fill: true,
data: [0, 0, 0, 0, 28.52, 28.53, 28.75, 29.02, 29.16, 30.42, 30.65, 31.29, 31.53, 31.93],
backgroundColor: '#1f78b4',
borderColor: '#1f78b4',
borderWidth: 1
}, {
label: 'Solus',
fill: true,
data: [0, 0, 0, 0, 0.42, 0.45, 0.61, 0.64, 0.92, 1.12, 1.08, 1.21, 1.23, 1.46],
backgroundColor: '#6a3d9a',
borderColor: '#6a3d9a',
borderWidth: 1
}]
...
var ct = document.getElementById('myChart').getContext('2d');
var Chart = new Chart(ct, {
type: 'line',
data: {
datasets: [{
label: 'test',
data:[1,10],
backgroundColor: "rgb(0, 0, 0, 0)",
borderColor: "rgb(0, 0, 255)",
pointBackgroundColor: "rgb(0, 0, 255)",
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
},
yAxes: [{
scaleLabel: {
display: true,
labelString: 'test'
}
}]
}
},
plugins: [{
beforeDraw: function(c) {
var legends = c.legend.legendItems;
legends.forEach(function(e) {
e.fillStyle = 'red';
});
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<canvas id="myChart" width="400" height="300"></canvas>
Related
I have a chart showing website calls the last 7 days. There it is:
Here is the initialization:
varwebsitecalls_chart = new Chart(websitecalls_chartel, {
type: 'line',
data: {
labels: ["Sa", "So", "Mo", "Di", "Mi", "Do", "Fr"],
datasets: [{
label: 'Websitecalls',
data: [0, 0, 0, 0, 0, 0, 0],
borderWidth: 2,
borderColor: '#00000',
backgroundColor: '#ffff',
pointStyle: 'circle',
pointRadius: 7,
cubicInterpolationMode: 'monotone',
tension: 0.4,
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
},
},
scales: {
x: {
grid: {
display: false,
}
},
y: {
min: 0,
ticks: {
precision: 0,
font: {
family: 'Montserrat',
},
},
grid: {
borderDash: [5, 5],
}
},
}
},
});
Problem:
The Problem is that the chart cuts of the circles when the data is 0 because I set min to 0. I have to set min to 0 because negative websitecalls can not exist. If I do not set it -1 will be displayed. Is there a way to fix this designtechnical issue?
Thanks in advance,
Filip.
No idea why, but defining y.beginAtZero: true instead of y.min: 0 will solve the problem.
Please take a look at your amended and runnable code and see how it works.
new Chart('websitecalls_chartel', {
type: 'line',
data: {
labels: ["Sa", "So", "Mo", "Di", "Mi", "Do", "Fr"],
datasets: [{
label: 'Websitecalls',
data: [0, 0, 0, 0, 0, 0, 0],
borderWidth: 2,
borderColor: '#00000',
backgroundColor: '#ffff',
pointStyle: 'circle',
pointRadius: 7,
cubicInterpolationMode: 'monotone',
tension: 0.4,
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
},
},
scales: {
x: {
grid: {
display: false,
}
},
y: {
beginAtZero: true,
ticks: {
precision: 0,
font: {
family: 'Montserrat',
},
},
grid: {
borderDash: [5, 5],
}
},
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
<canvas id="websitecalls_chartel" height="80"></canvas>
Chart.js documentation states...
beginAtZero: if true, scale will include 0 if it is not already included.
min: user defined minimum number for the scale, overrides minimum value from data.
working on a Chart.JS line chart. Renders fine, tooltips are fine, but when I hover over a line (not the chart as a whole, just a line) I get
Chart.js:1647 Uncaught TypeError: this.setValues is not a function
at new Color (Chart.js:1647)
at Color (Chart.js:1629)
at Object.helpers$1.color (Chart.js:10902)
at helpers$1.getHoverColor (Chart.js:10909)
at ChartElement.setHoverStyle (Chart.js:6137)
at Chart.updateHoverStyle (Chart.js:10143)
at Chart.handleEvent (Chart.js:10241)
at Chart.eventHandler (Chart.js:10167)
at listener (Chart.js:10100)
at HTMLCanvasElement.proxies.<computed> (Chart.js:7830)
This is using the lib inside a React functional component with import {Chart} from 'chart.js' and rendering in a hook, so:
useEffect(() => {
lineChart = new Chart(canvasRef.current, {
type: 'line',
options: {
title: {
display: true,
text: props.unitCode,
},
tooltips: {
mode: 'index',
intersect: false,
},
hover: {
mode: 'nearest',
intersect: true
},
maintainAspectRatio: false,
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time'
},
type: 'time',
distribution: 'linear',
}
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Mw'
}
}
]
}
},
data: {
datasets: [
{
label: 'PN Mw',
data: series[MetricType.Pn],
borderColor: 'rgb(0, 0, 0)',
fill: 'rgb(0, 0, 0)',
backgroundColor: 'rgb(0, 0, 0)',
pointRadius: 0,
borderWidth: 1,
lineTension: 1,
showLine: true,
},
{
label: 'MEL Mw',
data: series[MetricType.Mel],
borderColor: 'rgb(255, 0, 0)',
fill: 'rgb(255, 0, 0)',
backgroundColor: 'rgb(255, 0, 0)',
pointRadius: 0,
borderWidth: 2,
lineTension: 1,
showLine: true
},
{
label: 'Mil Mw',
data: series[MetricType.Mil],
borderColor: 'rgb(0, 0, 255)',
fill: 'rgb(0, 0, 255)',
backgroundColor: 'rgb(0, 0, 255)',
pointRadius: 0,
borderWidth: 2,
lineTension: 1,
showLine: true
}
]
}
});
})
The latest version of ChartJS, React, Chrome. Also happens in Firefox, so not browser-specific.
Ideas welcome :-)
Thanks
I want to draw a reference line in the time series chart. I tried to use two x axis and two y axis so I have like two graphs in one. the one would show time scale line and the other one would show linear line (reference line).
image example of what I want it to look like :
I tried in fiddle but its like the two X axis are connected and not scaling separately:
var data = {
labels: [],
datasets: [{
fill: 'none',
label: 'signal1',
backgroundColor: 'rgba(75,192,192,0.4)',
pointRadius: 5,
pointHoverRadius: 7,
borderColor: 'rgba(75,192,192,1)',
borderWidth: 1,
lineTension: 0,
xAxisID: 'xAxis1',
yAxisID: 'yAxis1',
data: [{
x: '2016-07-17',
y: 44
},
{
x: '2016-07-19',
y: 50
},
{
x: '2016-07-22',
y: 84
},
],
}, {
fill: 'none',
label: 'signal2',
lineTension: 0,
xAxisID: 'xAxis2',
yAxisID: 'yAxis2',
data: [{
x: 0,
y: 55
},
{
x: 1,
y: 55
},
]
}],
};
var option = {
scales: {
yAxes: [{
id: 'yAxis1',
offset: true,
gridLines: {
color: 'rgba(151, 151, 151, 0.2)',
display: true,
zeroLineColor: '#979797',
zeroLineWidth: 1,
tickMarkLength: 15,
drawBorder: true,
},
ticks: {
beginAtZero: false,
padding: 5,
fontSize: 12,
fontColor: '#222222',
},
}, {
id: 'yAxis2',
gridLines: {
color: 'rgba(151, 151, 151, 0.2)',
display: false,
drawBorder: false,
},
ticks: {
beginAtZero: false,
},
}],
xAxes: [{
id: 'xAxis1',
offset: true,
bounds: 'data',
type: 'time',
distribution: 'linear',
time: {
unit: 'day',
displayFormats: {
day: 'D.M.YYYY',
},
},
gridLines: {
display: true,
color: 'rgba(151, 151, 151, 0.2)',
zeroLineColor: '#979797',
zeroLineWidth: 1,
tickMarkLength: 5,
drawBorder: true,
},
ticks: {
source: 'auto',
autoSkip: true,
autoSkipPadding: 30,
maxRotation: 0,
padding: 2,
fontSize: 12,
fontColor: '#222222',
},
}, {
id: 'xAxis2',
type:"linear",
gridLines: {
display: false,
drawBorder: false,
},
}]
}
};
https://jsfiddle.net/2gyk9v5e/15/
This can be done with the Plugin Core API. The API offers different hooks that may be used for executing custom code. In your case, you can use the afterDraw hook to draw the desired lines directly on the canvas using CanvasRenderingContext2D.
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
chart.data.datasets[0].refLines.forEach(r => {
var y = yAxis.getPixelForValue(r.y);
ctx.strokeStyle = r.color;
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
});
ctx.restore();
}
}],
Above code assumes that the reference lines are defined inside your dataset through the following definition.
refLines: [
{ y: 45, color: '#0be059' },
{ y: 49, color: '#fc3503' }
]
Please take a look at your amended code and see how it works.
new Chart('canvas', {
type: 'line',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
chart.data.datasets[0].refLines.forEach(r => {
var y = yAxis.getPixelForValue(r.y);
ctx.strokeStyle = r.color;
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
datasets: [{
fill: false,
label: 'signal1',
backgroundColor: 'rgba(75,192,192,0.4)',
pointRadius: 5,
pointHoverRadius: 7,
borderColor: 'rgba(75,192,192,1)',
borderWidth: 1,
lineTension: 0,
data: [
{ x: '2016-07-14', y: 44 },
{ x: '2016-07-15', y: 52 },
{ x: '2016-07-16', y: 45 },
{ x: '2016-07-17', y: 47 },
{ x: '2016-07-18', y: 35 },
{ x: '2016-07-19', y: 46 },
{ x: '2016-07-20', y: 50 },
{ x: '2016-07-21', y: 44 }
],
refLines: [
{ y: 45, color: '#0be059' },
{ y: 49, color: '#fc3503' }
]
}]
},
options: {
scales: {
yAxes: [{
gridLines: {
color: 'rgba(151, 151, 151, 0.2)',
display: false,
drawBorder: false
}
}],
xAxes: [{
offset: true,
bounds: 'data',
type: 'time',
time: {
unit: 'day',
displayFormats: {
day: 'D.M.YYYY',
}
},
gridLines: {
color: 'rgba(151, 151, 151, 0.2)',
zeroLineColor: '#979797',
zeroLineWidth: 1,
tickMarkLength: 5,
drawBorder: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<canvas id="canvas" height="80"></canvas>
Is it possible to bring the "ticks" of the Chart.js radar graph to the foreground so that they would be on top of the graph itself?
I don't see anything related to this issue in the official documentation.
Method that renders this:
const chart = new Chart(ctx, {
type: 'polarArea',
data: {
labels: ['Silver', 'Palladium', 'Platinum', 'Gold'],
datasets: [
{
label: 'Points',
pointRotation: 45,
backgroundColor: [
color(this.chartColors.grey).rgbString(),
color(this.chartColors.green).rgbString(),
color(this.chartColors.blue).rgbString(),
color(this.chartColors.yellow).rgbString(),
],
data: [0, 0, 0, 0],
borderWidth: 0,
pointBackgroundColor: 'rgba(0, 0, 0, 1)'
}
],
},
options: {
responsive: true,
animation: {
animateRotate: true
},
layout: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
},
scale: {
ticks: {
fontColor: '#000000',
mirror: true,
}
},
legend: {
position: 'top'
},
}
});
One workaround is to make these fill colours transparent like so:
color(this.chartColors.yellow).alpha(0.5).rgbString(),
...this way the ticks would be somewhat acceptably visible. However, I'd rather have them fully saturated. Thanks in advance for any suggestions.
You can define the scale.ticks.z option as as documented here.
scale: {
ticks: {
...
z: 1
}
},
z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top.
Please have a look at your amended code below:
const chart = new Chart('myChart', {
type: 'polarArea',
data: {
labels: ['Silver', 'Palladium', 'Platinum', 'Gold'],
datasets: [
{
label: 'Points',
pointRotation: 45,
backgroundColor: ['grey', 'green', 'blue', 'yellow'],
data: [3, 4, 8, 9],
borderWidth: 0,
pointBackgroundColor: 'rgba(0, 0, 0, 1)'
}
]
},
options: {
responsive: true,
animation: {
animateRotate: true
},
layout: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
},
scale: {
ticks: {
fontColor: '#000000',
mirror: true,
z: 1
}
},
legend: {
position: 'top'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="100"></canvas>
I'm running ChartJS 2.7.something, and have https://codepen.io/anon/pen/YBJWKX this graph. Code below:
var ctx = document.getElementById("chart");
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
draw: function(ease) { Chart.controllers.line.prototype.draw.call(this, ease);
if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
let activePoint = this.chart.tooltip._active[0],
ctx = this.chart.ctx,
x = activePoint.tooltipPosition().x,
topY = this.chart.scales['y-axis-a'].top,
bottomY = this.chart.scales['y-axis-a'].bottom;
// draw line
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 1;
ctx.strokeStyle = 'rgba(52,58,64,1)';
ctx.stroke();
ctx.restore();
}
}
});
var dashboard = new Chart(ctx, {
type: 'bar',
data: {
responsive: true,
labels: ["1", "2", "3", "4", "5", "6", "7"],
datasets: [{
label: 'No Data',
yAxisID: 'y-axis-a',
data: [null,null,4000, null, null, 4000, null],
backgroundColor: 'rgba(229,229,229,1)',
borderWidth: '0',
borderSkipped: 'bottom',
hoverBackgroundColor: 'rgba(229,229,229,1)',
hoverBorderWidth: '0',
showTooltip: false,
},
{
type: 'LineWithLine',
label: 'Dummy 1',
fill: false,
yAxisID: 'y-axis-a',
data: [2586, 2343, 2380, 2439, 3316, 1570, 3439],
borderColor: 'rgba(220,53,69,1)',
backgroundColor: 'rgba(220,53,69,1)',
borderWidth: 3,
borderCapStyle: 'butt',
pointRadius: 2,
pointBorderWidth: 0,
pointHoverRadius: 1,
pointHitRadius: 10,
lineTension: 0.1, // Removes Bezier on Line
},
{
type: 'LineWithLine',
label: 'Dummy 2',
fill: false,
yAxisID: 'y-axis-a',
data: [3466, 1295, 3015, 2351, 3305, 1167, 1350],
borderColor: 'rgba(40, 167, 69,1)',
backgroundColor: 'rgba(40, 167, 69,1)',
borderWidth: 3,
borderCapStyle: 'butt',
pointRadius: 2,
pointBorderWidth: 0,
pointHoverRadius: 1,
pointHitRadius: 10,
lineTension: 0.1, // Removes Bezier on Line
},
{
type: 'LineWithLine',
label: 'Dummy 3',
fill: false,
yAxisID: 'y-axis-b',
data: [1, 8, 17, 6, 12, 4, 7],
borderColor: 'rgba(0, 123, 255,1)',
backgroundColor: 'rgba(0, 123, 255,1)',
borderWidth: 3,
borderCapStyle: 'butt',
pointRadius: 2,
pointBorderWidth: 0,
pointHoverRadius: 1,
pointHitRadius: 10,
lineTension: 0.1, // Removes Bezier on Line
},
]
},
options: {
bezierCurve: false,
maintainAspectRatio:false,
legend: {
labels: {
filter: function(item, dashboard) {
// Logic to remove a particular legend item goes here
return !item.text.includes('No Data');
}
}
},
tooltips: {
mode: 'index',
intersect: false,
//enabled: false,
},
scales: {
yAxes: [{
position: "left",
id: "y-axis-a",
ticks: {
suggestedMax: 3600,
}
},
{
position: "left",
id: "y-axis-b",
max: 25,
display: false,
gridLines: {
display:false
}
}],
},
xAxes: [{ }]
}
});
It's almost perfect for what i need it for, however:
In this particular case I would like to remove the "No Data" grey bars from the tooltip where it's values are NULL.
At the same time, i would also like to remove the tooltip entirely where it's value is not NULL, ie disabled.
I'm using the tooltip in index mode with intersect turned to false so that the black line that is on the graph works, the black line came from this question.
I've tried a range of things from stackoverflow, but I have a feeling that none of the things i've tried have worked because of how the tooltip is set up.
Is it at all possible to do what I need here?
Just change your options parameter a little bit.
tooltips: {
mode: 'index',
intersect: false,
callbacks: {
//returns a empty string if the label is "No Data"
label: function(items, data){
let dataset = data.datasets[items.datasetIndex];
if(dataset.label !== "No Data") {
return `${dataset.label}: ${items.yLabel}`
} else {
return ""
}
},
//only returns something when at least one dataset yLabel is a valid number.
title: function(t, e) {
let shouldDisplay = false;
t.forEach((it) => {
if(!isNaN(it.yLabel)) shouldDisplay = true;
});
if(shouldDisplay) {
return t[0].xLabel;
}
}
}
},
There is probably a better way to optimize this, but I hope it helps