Bubble getting cut off in Chart.js - chart.js

I'm having an issue where the last bubble in my chart cuts off. I need a way of extending the chart so that the entire circle is displayed. I've tried everything from adding an extra value to the end, to adjusting padding. Nothing seems to work. Unfortunately, the Chart JS documention on bubble charts is severely lacking as well.
var randomScalingFactor = function() {
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
};
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function() {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
};
var bubbleChartData = {
animation: {
duration: 10000
},
datasets: [{
label: "My First dataset",
backgroundColor: randomColor(),
data: [
{
x: 10,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 20,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 30,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 40,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 50,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 60,
y: 0,
r: Math.abs(randomScalingFactor()) / 5,
}, {
x: 70,
y: 0,
r: 30,
}]
}]
};
var ctx = document.getElementById('Chart').getContext('2d');
var chart = new Chart(ctx, {
type: 'bubble',
data: bubbleChartData
})
JSFiddle: https://jsfiddle.net/3dog0bec/

I solved this issue by modifying the xAxes ticks min and max. This worked because I have a set number of data to display, so I simply set the values to 10 less than the first data point and 10 more than the last.
var chart = new Chart(ctx, {
type: 'bubble',
data: bubbleChartData,
options: {
scales: {
xAxes: [
{
ticks: {
min: -10,
max: 100
}
}]
}
}
});

Related

Chart.js Tooltip over the line not only over points

I have a line chart in chart.js. Now I would like to have a tooltip, when I hover over the line not just when i hover over a point.
The tooltip should then be displayed at the position of the mouse.
Is this somehow possible?
I have tried all interaction.mode options, but non of them have this behavior.
Thank You
You can use the croshair plugin for this if you are still using v2 of the lib: https://chartjs-plugin-crosshair.netlify.app/
Live example:
var ctx = document.getElementById("myChart");
function generateDataset(shift, label, color) {
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
let data = [];
let index = 0;
while (index < 5) {
data.push({
x: index,
y: getRandomInt(10, 40)
});
index++;
}
var dataset = {
backgroundColor: color,
borderColor: color,
showLine: true,
fill: false,
pointRadius: 2,
label: label,
data: data,
lineTension: 0,
interpolate: true,
xAxisID: 'x-axis-0'
};
return dataset;
}
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D', 'E'],
datasets: [
generateDataset(0, "A", "red"),
generateDataset(1, "B", "green")
]
},
options: {
scales: {
xAxes: [{
display: true,
type: 'linear',
position: 'bottom',
id: 'x-axis-0',
ticks: {
source: 'labels'
}
}]
},
tooltips: {
mode: "interpolate",
intersect: false,
callbacks: {
title: function(a, d) {
// return a[0].xLabel.toFixed(2);
return a[0].xLabel;
},
label: function(i, d) {
return (
d.datasets[i.datasetIndex].label + ": " + i.yLabel.toFixed(2)
);
}
}
},
plugins: {
crosshair: {
line: {
// Add alpha chanel so line becomes transparant so you dont see it
color: '#ffffff00',
},
sync: {
enabled: false
}
}
}
}
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"></canvas>
</div>
<script src="https://npmcdn.com/chart.js#2.9.4/dist/Chart.bundle.min.js"></script>
<script src="https://unpkg.com/chartjs-plugin-crosshair#1.1.6/dist/chartjs-plugin-crosshair.js"></script>
</body>
</html>
If you are using V3 of the lib you will need to use a custom own plugin
Example:
// Options for the indicators
const indicatorOptions = {
radius: 4, borderWidth: 1, borderColor: 'red', backgroundColor: 'transparent'
};
// Override getLabelAndValue to return the interpolated value
const getLabelAndValue = Chart.controllers.line.prototype.getLabelAndValue;
Chart.controllers.line.prototype.getLabelAndValue = function(index) {
if (index === -1) {
const meta = this.getMeta();
const pt = meta._pt;
const vScale = meta.vScale;
return {
label: 'interpolated',
value: vScale.getValueForPixel(pt.y)
};
}
return getLabelAndValue.call(this, index);
}
// The interaction mode
Chart.Interaction.modes.interpolate = function (chart, e, option) {
const x = e.x;
const items = [];
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
const pt = meta.dataset.interpolate({ x }, "x");
if (pt) {
const element = new Chart.elements.PointElement({...pt, options: {...indicatorOptions}});
meta._pt = element;
items.push({element, index: -1, datasetIndex: meta.index });
} else {
meta._pt = null;
}
}
return items;
};
// Plugin to draw the indicators
Chart.register({
id: 'indicators',
afterDraw(chart) {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
if (meta._pt) {
meta._pt.draw(chart.ctx);
}
}
},
afterEvent(chart, args) {
if (args.event.type === 'mouseout') {
const metas = chart.getSortedVisibleDatasetMetas();
for (let i = 0; i < metas.length; i++) {
metas[i]._pt = null;
}
args.changed = true;
}
}
})
var ctx = document.getElementById("myChart").getContext("2d");
var chart = new Chart(ctx, {
type: "line",
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
fill: true,
label: "My First dataset",
backgroundColor: "rgba(132, 0, 0, 1)",
borderColor: "rgb(255, 99, 132)",
data: [0, 10, 5, 2, 20, 30, 45]
},
{
data: [30, 40, 50],
label: 'My Second Dataset',
fill: true,
backgroundColor: "lightgreen",
borderColor: "green"
}
]
},
options: {
interaction: {
mode: "interpolate",
intersect: false,
axis: "x"
},
plugins: {
tooltip: {
position: 'nearest',
displayColors: false,
}
}
},
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<h1>Interpolating line values</h1>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.3.2/chart.js"></script>
</div>

How to get a square grid in Chart.js with responsive scatter chart?

I am trying to create a scatter chart in chart.js where the grid spacing in x will always be equal to the grid spacing in y and the appearance of the grid is truly square, regardless of canvas size or aspect ratio. The chart represents physical space, so I don't want the x and y coordinates to be elongated in either direction or it will appear stretched to the eye.
I'm also looking for a responsive solution since I can't fix the min, max, or grid spacing in either axis as the plotted value ranges change with user input. One thing is for sure though: the canvas size will always be wider than it is tall.
I've tried playing around with grid spacing and aspect ratios but I'm not getting what I need. I was able to get the gridlines truly square only by plotting a dummy point with the y-value at the same magnitude as the maximum x-value, but that essentially just forced the canvas to be square and left too much unused white space at the top.
As an example, the graph might need to be 2000 units wide on the x-axis and 500 units high on the y-axis. Grid spacing would be say, 100 units for both axes. The canvas in this case would be about four times wider than high, but the grid lines should all appear square, rather than rectangular.
Any ideas?
You can use the scriptable options to calculate the amount of ticks you need with a given spacing to get squares, it need some fine tuning because of rounding but this will give you a rough idea/setup to finetune
Example:
const spacing = 50;
var options = {
type: 'scatter',
data: {
datasets: [{
label: '# of Votes',
data: [{
x: 0,
y: 4
}, {
x: 2,
y: 2
}, {
x: 4,
y: 0
}],
borderWidth: 1,
backgroundColor: 'blue'
},
{
label: '# of Points',
data: [{
x: 2,
y: 3
}, {
x: 6,
y: 8
}, {
x: 0,
y: 3
}],
borderWidth: 1,
backgroundColor: 'red'
}
]
},
options: {
scales: {
y: {
ticks: {
count: (ctx) => {
const {
scale: {
maxHeight
}
} = ctx;
return maxHeight / spacing;
}
}
},
x: {
ticks: {
count: (ctx) => {
const {
scale: {
maxWidth
}
} = ctx;
return maxWidth / spacing;
}
}
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.3.2/chart.js"></script>
</body>

Responsive legend font size in react-chartjs 2

I have tried to make a pie chart using react-chartjs 2, It is working fine in desktop view but in mobile view the legend is not responsive it is taking much space and due to this the size of pie chart become very small.
my code:
function Chart(props: any) {
const option = {
tooltips: {enter image description here
callbacks: {
label: function (tooltipItem: any, data: any) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var meta = dataset._meta[Object.keys(dataset._meta)[0]];
var total = meta.total;
var currentValue = dataset.data[tooltipItem.index];
var percentage = parseFloat(
((currentValue / total) * 100).toFixed(1)
);
return currentValue + " (" + percentage + "%)";
},
title: function (tooltipItem: any, data: any) {
return data.labels[tooltipItem[0].index];
},
},
},
legend: {
display: true,
labels: {
fontSize: 12,
},
position: "right",
},
};
return (
<div className="chart">
<Pie data={props.ChartData} options={option} />
</div>
);
You can set your fontSize object as a ternery operator that checks the widts (or something else) to see if you are on a mobile device and give back the right fontSize according to it
If you want to update it real time because screen sizes change you can do that by mutating the chart options itself in a resizeEvent listner
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
legend: {
labels: {
fontSize: window.innerWidth > 350 ? 20 : 10
}
},
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
const chart = new Chart(ctx, options);
window.addEventListener('resize', () => {
if (window.innerWidth < 350) {
chart.options.legend.labels.fontSize = 10;
} else {
chart.options.legend.labels.fontSize = 20
}
});
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>
</body>
Here is the way to have a responsive legend font size (also explained here)
options: {
plugins: {
legend: {
labels: {
// This more specific font property overrides the global property
font: {
size: 14
}
}
}
}
}

how to fix this google charts bar chart error

same data are used to draw line chart and bar chart. it works fine for drawing line. but there is an error for drawing bar chart. the error is "a.getTime is not a function×". any suggestions are appreciated.
here is my code:
datasource.addColumn('date', 'Date');
datasource.addColumn('number', 'quantity');
datasource.addColumn('number', 'quantity');
datasource.addColumn('number', 'quantity');
for (n = 0; n < tem.length; n++) {
datasource.addRows([[new Date(tem[n][0]), tem[n][1], tem[n][2], tem[n][3]]]);
}
*********line chart ***********
chart = new google.visualization.LineChart(document.getElementById('chart_div'));
var options = {
actions: ['dragToZoom', 'rightClickToReset'],
interpolateNulls: true, //bypass null
legend: 'none',
crosshair: { trigger: 'both', opacity: 0.5 }, // Display crosshairs on focus and selection.
title: 'Company Performance',
hAxis: { title: 'Year', titleTextStyle: { color: 'red' }, format: "YY.MM.dd", slantedText: true, slantedTextAngle: 30 },
vAxis: {
viewWindowMode: 'explicit',
viewWindow: {
max: maxy,
min: 0
}
},
explorer: { axis: 'horizontal', maxZoomOut: 1 } //wheel zoom
};
chart.draw(datasource, options);
***********bar chart*************
chart = new google.visualization.BarChart(document.getElementById('chart_div'));
var options = {
title: 'Company Performance',
hAxis: { title: 'Number', titleTextStyle: { color: 'red' } },
vAxis: { minValue: 0 },
explorer: { axis: 'horizontal', maxZoomOut: 1 }
};
chart.draw(datasource, options);*

How to compute x,y values from a click event using x,y pixel coordinates in ChartJS?

I would like to receive the x,y values from a line chart in ChartJS, when the user clicks on the Chart.
Die difficulty compared to usually discusses techniques is, that I need to receive x,y values from any position in the chart and not just when I click on some samples in the dataset.
So when I click on some empty place, I need to know its x and y axis values.
Got some example with a comment therein, where I sutck:
var randomInt = function() {
return (Math.random() * 100) - 50;
};
//needs it for the test
var labels = ["Management", "Finance", "Human Resources", "Business Development and Marketing", "Information Technology", "Professional Development and Training", "Knowledge Management", "Logistics", "Support", "Business Services", "Other"];
var datas = [{
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}, {
x: randomInt(),
y: randomInt(),
}];
var scatterChartData = {
labels: labels,
datasets: [{
label: "My First dataset",
data: datas
}]
};
var ctx = document.getElementById("canvas").getContext("2d");
var myScatter = new Chart.Scatter(ctx, {
data: scatterChartData,
options: {
title: {
display: true,
text: 'Chart.js Scatter Chart'
},
scales: {
xAxes: [{
position: 'bottom',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'x axis'
}
}],
yAxes: [{
position: 'right',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'y axis'
}
}]
}
}
});
//
canvas.onclick = function(evt){
//HERE I have the x,y pixel coordinates inside the evt object.
//How to get the x,y values from the x and y axis??
//this works only if I click on one of the samples...
var activePoints = myScatter.getElementsAtEvent(evt);
var firstPoint = activePoints[0];
if(firstPoint !== undefined){
var label = myScatter.data.labels[firstPoint._index];
var value = myScatter.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
alert(label + ": " + value.x);
alert(label + ": " + value.y);
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0-beta2/Chart.min.js"></script>
<div>
<div>
<canvas id="canvas"></canvas>
</div>
</div>
I found at least a way to extract the y-axis value. The computation of the x-axis value should be similar:
//inside the click callback...
var scaler=that.chart.scales['y-axis-0'];
var chart_height_px=scaler.bottom+scaler.top;
var y=evt.clientY-that.canvas[0].getBoundingClientRect().top-scaler.top;
var yval=scaler.max-y/scaler.height*(scaler.max-scaler.min);
console.log("value clicked: %o, ypx: %o", yval, y);
that.trigger("onTickerYClick", yval);