Related
I'm having some trouble trying to change the backgroundColor of a specific chart area between two yAxis ticks.This is what I have so far:
And this is what I wanted:
I've seen some similar posts about that and people recommend using Annotation to do this. I tried using it on my chart and it didn't work. This is my first time building a chart with chart.js so I'm still learning. Here's my code:
var profileChart = new Chart(ctx1, {
type: "line",
data: {
labels: ["", "D", "I", "S", "C", ""],
datasets:[{
data: [],
borderWidth: 1,
pointBackgroundColor: "black",
backgroundColor: "black",
borderColor: "black",
fill: false,
lineTension: 0,
yAxisID: 'first-y-axis'
},
{
yAxisID: 'third-y-axis'
}],
},
options: {
title: {
display: true,
text: 'Gráfico do Perfil DISC',
fontSize: 20,
},
scales: {
yAxes: [{
id: 'first-y-axis',
type: 'linear',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
padding: '15px',
labelString: 'Intensity'
},
ticks: {
max: 28,
min: 1,
stepSize: 1
}
},
{
id: 'second-y-axis',
type: 'linear',
position: 'left',
gridLines: {
drawOnChartArea: true
},
ticks: {
display: false,
min: 1,
max: 8,
stepSize: 1
}
},
{
id: 'third-y-axis',
position: 'right',
type: 'linear',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
padding: '10px',
labelString: 'Segment'
},
ticks: {
max: 7.5,
min: 0.5,
stepSize: 1
},
afterTickToLabelConversion: function(scaleInstance) {
scaleInstance.ticks[0] = null;
scaleInstance.ticks[scaleInstance.ticks.length - 1] = null;
scaleInstance.ticksAsNumbers[0] = null;
scaleInstance.ticksAsNumbers[scaleInstance.ticksAsNumbers.length - 1] = null;
},
}]
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return tooltipItem.yLabel;
}
}
}
},
annotation: {
drawTime: "afterDraw",
annotations: [{
id: 'box1',
type: 'box',
yScaleID: 'second-y-axis',
yMin: 12.5,
yMax: 16.5,
backgroundColor: 'grey',
}]
}
});
You can draw the rectangle directly on the canvas using the Plugin Core API. The API offers a range of hooks that may be used for performing custom code.
In your amended code below, I use the beforeDraw hook to draw the rectangle through CanvasRenderingContext2D.fillRect().
var profileChart = new Chart('canvas', {
type: "line",
plugins: [{
beforeDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['first-y-axis'];
ctx.save();
ctx.fillStyle = 'lightgray';
ctx.beginPath();
var yTop = yAxis.getPixelForValue(16.5);
var yBottom = yAxis.getPixelForValue(12.5);
ctx.fillRect(xAxis.left, yTop, xAxis.right - xAxis.left, yBottom - yTop);
ctx.stroke();
ctx.restore();
}
}],
data: {
labels: ["", "D", "I", "S", "C", ""],
datasets: [{
data: [,25.5, 8, 7.5, 11],
borderWidth: 1,
pointBackgroundColor: "black",
backgroundColor: "black",
borderColor: "black",
fill: false,
lineTension: 0,
yAxisID: 'first-y-axis'
},
{
yAxisID: 'third-y-axis'
}
],
},
options: {
title: {
display: true,
text: 'Gráfico do Perfil DISC',
fontSize: 20,
},
scales: {
yAxes: [{
id: 'first-y-axis',
type: 'linear',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
padding: '15px',
labelString: 'Intensity'
},
ticks: {
max: 28,
min: 1,
stepSize: 1
}
},
{
id: 'second-y-axis',
type: 'linear',
position: 'left',
gridLines: {
drawOnChartArea: true
},
ticks: {
display: false,
min: 1,
max: 8,
stepSize: 1
}
},
{
id: 'third-y-axis',
position: 'right',
type: 'linear',
gridLines: {
drawOnChartArea: false
},
scaleLabel: {
display: true,
padding: '10px',
labelString: 'Segment'
},
ticks: {
max: 7.5,
min: 0.5,
stepSize: 1
},
afterTickToLabelConversion: function(scaleInstance) {
scaleInstance.ticks[0] = null;
scaleInstance.ticks[scaleInstance.ticks.length - 1] = null;
scaleInstance.ticksAsNumbers[0] = null;
scaleInstance.ticksAsNumbers[scaleInstance.ticksAsNumbers.length - 1] = null;
},
}
]
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return tooltipItem.yLabel;
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<canvas id="canvas" height="200">
I am working on chart.js for implementing stack bar graph. That graph has few issues like
Not showing tick size vertically at left
Showing unwanted horizontal gridlines
Displaying horizontal thick line on top instead of bottom
This is my code
public stackbar()
{
Chart.defaults.global.datasets.bar.barPercentage = 0.5;
Chart.defaults.global.datasets.bar.categoryPercentage = 0.5;
var colors = ['#299DFF','#80FFFF','#F8362B',];
var chBar = document.getElementById("mychart");
var chartData = {
labels: ["Product"],
datasets: [{
label: 'P2',
data: [29566],
backgroundColor: colors[0]
},
{
label: 'P3',
data: [O2],
backgroundColor: colors[1]
},
{
label: 'P4',
data: [3],
backgroundColor: colors[2]
}
]
};
if (chBar) {
new Chart(chBar, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes: [{
gridLines: {
display:false
},
//barPercentage: 0.5,
//categoryPercentage: 0.5
barPercentage: 0.5,
categoryPercentage: 0.5
}
],
yAxes: [{
gridLines: {
display:true
},
type: 'logarithmic',
ticks: {
beginAtZero: true,
userCallback: (value, index) => {
const remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (remain == 1 || remain == 2 || remain == 5 || index == 0) {
return value.toLocaleString();
}
return '';
},
suggestedMax: 80,
padding: 25
}
}]
},
legend: {
display: true,
maxWidth: 100,
padding:30,
fullWidth:true,
position: 'bottom',
lineHeight: 12,
justifyContent: 'space-between',
labels: {
fontSize: 10,
usePointStyle: true
}
},
}
});
}
This is the screenshot
How can I fix these issues?
The horizontal grid lines can be removed through the following configuration in the chart options:
yAxes: [{
gridLines: {
display: false
},
I changed this in your code and removed a few unnecessary definitions and it looks just fine to me as you can see in the following runnable code.
var colors = ['#299DFF', '#80FFFF', '#F8362B'];
var chartData = {
labels: ["Product"],
datasets: [{
label: 'P2',
data: [29566],
backgroundColor: colors[0]
},
{
label: 'P3',
data: [2],
backgroundColor: colors[1]
},
{
label: 'P4',
data: [3],
backgroundColor: colors[2]
}
]
};
new Chart('myChart', {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes: [{
barPercentage: 0.5,
categoryPercentage: 0.5
}
],
yAxes: [{
gridLines: {
display: false
},
type: 'logarithmic',
ticks: {
beginAtZero: true,
userCallback: (value, index) => {
const remain = value / (Math.pow(10, Math.floor(Chart.helpers.log10(value))));
if (remain == 1 || remain == 2 || remain == 5 || index == 0) {
return value.toLocaleString();
}
return '';
}
}
}]
},
legend: {
display: true,
maxWidth: 100,
padding: 30,
fullWidth: true,
position: 'bottom',
lineHeight: 12,
justifyContent: 'space-between',
labels: {
fontSize: 10,
usePointStyle: true
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
<canvas id="myChart" height="150"></canvas>
This is how options for my chart look like
options: {
legend: {
display: false
},
scales: {
xAxes: [
{
type: "time",
gridLines: {
display: false
},
ticks: {
padding: 15
}
}
],
yAxes: [
{
ticks: {
padding: 22,
min: 1,
max: 5,
stepSize: 1
},
position: "left",
gridLines: {
drawTicks: false
}
}
]
}
}
as you can see there is an option
gridLines: {
display: false
},
because of this there is no vertical lines on the chart.
What I want to do is to add only one vertical line on the end of the chart(right side of the chart)
Here is my live example https://codesandbox.io/s/line-chart-with-vanilla-js-and-chartjs-pi9fn?file=/src/index.js
This can be solved as follows:
Define data.labels in the chart configuration. Given your data, this could be done by extracting the x values of of the data objects using Array.map().
const labels = data.map(o => o.x);
Then you need to adapt xAxis.ticks in order to instruct Chart.js that ticks (including grid lines) have to be generated from user given labels (see Tick Source from Chart.js documentation).
ticks: {
source: 'labels'
}
Further you have to change the definition of xAxis.gridLines. Make them displayed again and define lineWidth instead. This should be an array of numbers that specify the stroke width of individual grid lines. Basically all 0 except 1 for the last number.
gridLines: {
lineWidth: labels.map((l, i) => i < labels.length - 1 ? 0 : 1)
}
Please have a look at the runnable code snippet below.
const data = [
{ x: "1-01", y: 1 },
{ x: "1-07", y: 3 },
{ x: "1-14", y: 2 },
{ x: "1-21", y: 4 },
{ x: "1-28", y: 5 },
{ x: "2-04", y: 1 }
];
const labels = data.map(o => o.x);
new Chart('line-chart', {
type: "scatter",
responsive: true,
maintainAspectRatio: false,
data: {
labels: labels,
datasets: [
{
data: data,
label: "A",
showLine: true,
fill: false,
borderWidth: 4,
pointRadius: 5,
tension: 0,
backgroundColor: "#ffffff",
borderColor: "red"
}
]
},
options: {
legend: {
display: false
},
scales: {
xAxes: [
{
type: 'time',
unit: 'day',
time: {
parser: 'M-D'
},
gridLines: {
lineWidth: labels.map((l, i) => i < labels.length - 1 ? 0 : 1)
},
ticks: {
source: 'labels',
padding: 15
}
}
],
yAxes: [
{
ticks: {
padding: 22,
min: 1,
max: 5,
stepSize: 1
},
position: "left",
gridLines: {
drawTicks: false
}
}
]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="line-chart" height="90"></canvas>
I have a created a histogram using chart.js. (Referring Make a Histogram in Chart.js). Now i need a zoom in functionality because by default if i am enabling zoom in chart labels and bars are not in-sync. And my data is too much in chart. so i need with width of bar.
This is my config object.
const config: any = {
type: 'bar',
data: {
labels: xLabelx,
datasets: [ { label: 'Count', backgroundColor: 'rgba(215, 239, 252,0.5)', data: NumberOfVehicle,
borderColor: '#37B1F0', borderWidth: 0.5, hoverBackgroundColor: '#66bcff' } ]
},
options: {
tooltips: {
mode: 'label',
bodyFontSize: 12,
opacity: 0.5,
borderWidth: 5,
callbacks: {
title: (tooltipItems, data) => { return ''; },
label(tooltipItem, data) {
const label = data.datasets[tooltipItem.datasetIndex].label;
const value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
return label + ': ' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '');
} } },
pan: { enabled: true, mode: 'xy', drag: true, speed: 10, threshold: 10 },
zoom: { enabled: true, drag: false, mode: 'x' },
maintainAspectRatio: false,
responsive: false,
legend: {
display: true,
onHover: (event, legendItem) => { document.getElementById('myChart').style.cursor = 'pointer'; },
labels: { fontSize: 14 }},
hover: {
onHover: (e, el) => { $('#myChart').css('cursor', el[0] ? 'pointer' : 'default'); }
},
scales: {
xAxes: [
{ display: false, barPercentage: 1.25, ticks: { max: xLabelx[xLabelx.length - 2] }},
{ display: false, ticks: { autoSkip: false, max: xLabelx[xLabelx.length - 1]}},
{ scaleLabel: { display: true, labelString: 'Range', fontFamily: 'Roboto', fontSize: 14 }}
],
yAxes: [
{ scaleLabel: { display: true, labelString: 'No. of model', fontFamily: 'Roboto', fontSize: 14 },
ticks: { beginAtZero: true, fontSize: 12, userCallback: (label, index, labels) => {
if (Math.floor(label) === label && label >= 0) { return label; } } } } ]
}
}
};
I am trying to achieve something like this using chart.js. I wanted to show data of male/female according to each age group:
Here is my chart options:
var options = {
layout: {
padding: {
top: 5,
}
},
scales:
{
yAxes: [{
display: true,
barPercentage: 0.4,
ticks: {
fontSize: 12
},
stacked: true,
}],
xAxes: [{
stacked: true,
}]
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: ageGroup,
datasets: [{
label: 'Male',
data: maleData,
backgroundColor: '#2196F3',
hoverBackgroundColor: '#2196F3'
},
{
label: 'Female',
data: femaleData,
backgroundColor: '#E91E63',
hoverBackgroundColor: '#E91E63'
}]
},
options: options
};
I changed the positive in femaleData array into a negative number to achieve the result above:
for (var i = 0; i < femaleData.length; i++) {
femaleData[i] = -Math.abs(femaleData[i]);
}
However, the y-axis at 0 is not centralized as it pushed to the right hand side since left hand side got more data. I not even sure if this is the correct way to set the chart in opposite direction. How can I do this correctly?
as per the requirements mentioned in OP's comment section
ꜱʜᴏᴡ ᴘᴏꜱɪᴛɪᴠᴇ ᴠᴀʟᴜᴇꜱ ᴏɴ x-ᴀxɪꜱ
use the following callback function for x-axis ticks :
callback: function(t, i) {
return t < 0 ? Math.abs(t) : t;
}
ꜱʜᴏᴡ ᴘᴏꜱɪᴛɪᴠᴇ ᴠᴀʟᴜᴇ ᴏɴ ᴛᴏᴏʟᴛɪᴘ
use the following callback function for tooltips :
callbacks: {
label: function(t, d) {
var datasetLabel = d.datasets[t.datasetIndex].label;
var xLabel = Math.abs(t.xLabel);
return datasetLabel + ': ' + xLabel;
}
}
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
var ageGroup = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61-70', '71-80', '80+'];
var maleData = [30, 0, 0, 0, 10, 0, 0, 0, 0];
var femaleData = [0, 0, 0, -20, -50, -20, 0, 0, 0];
var options = {
layout: {
padding: {
top: 5,
}
},
scales: {
yAxes: [{
display: true,
barPercentage: 0.4,
ticks: {
fontSize: 12
},
stacked: true,
}],
xAxes: [{
stacked: true,
ticks: {
callback: function(t, i) {
return t < 0 ? Math.abs(t) : t;
}
}
}]
},
tooltips: {
callbacks: {
label: function(t, d) {
var datasetLabel = d.datasets[t.datasetIndex].label;
var xLabel = Math.abs(t.xLabel);
return datasetLabel + ': ' + xLabel;
}
}
},
responsive: true,
//maintainAspectRatio: false,
legend: {
display: false,
},
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: ageGroup,
datasets: [{
label: 'Male',
data: maleData,
backgroundColor: '#2196F3',
hoverBackgroundColor: '#2196F3'
}, {
label: 'Female',
data: femaleData,
backgroundColor: '#E91E63',
hoverBackgroundColor: '#E91E63'
}]
},
options: options
};
new Chart(ctx, opt);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>