Change size of bubble radius without changing r value - javascript

I'm adding data points to a bubble graph. However, since the value for r is very small I can barely see the points on the graph. I tried to use the radius property to expand the radius, but it seems to be overwritten by the r data property. How can I increase the radius for each bubble? For example, how can I set them to 10x the actual r value?
This is the code that I have so far:
this.managers.forEach(manager => {
const newDataPoint = {
label: [manager.SecurityName],
backgroundColor: this.getRandomRGB(),
borderColor: this.getRandomRGB(),
data: [{
x: +manager[this.selectedX],
y: +manager[this.selectedY],
r: +manager[this.selectedR]
}],
radius: (+manager[this.selectedR] * 10)
};
this.chartDataSet.push(newDataPoint);
});
I've managed to change the size by multiplying the r property directly, but that changes the actual value that shows up when hovered, which I want to avoid. How can I keep the r property the same but still increase the radius?

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 could use the afterDatasetUpdate hook to increase the radius of the points in the dataset's metadata.
plugins:[{
afterDatasetUpdate: chart => {
chart.getDatasetMeta(0).data.forEach(v => {
v._model.radius *= 10;
v._options.hoverRadius = v._model.radius;
})
}
}],
new Chart('canvas', {
type: 'bubble',
plugins: [{
afterDatasetUpdate: chart => {
chart.getDatasetMeta(0).data.forEach(v => {
v._model.radius *= 10;
v._options.hoverRadius = v._model.radius;
})
}
}],
data: {
datasets: [{
label: 'First Dataset',
data: [
{x: 10, y: 20, r: 1 },
{x: 20, y: 10, r: 2 }
],
backgroundColor: 'rgb(255, 99, 132)'
}]
},
options: {
scales: {
xAxes: [{
ticks: {
min: 5,
max: 25
}
}],
yAxes: [{
ticks: {
min: 5,
max: 25
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="canvas" height="90"></canvas>

For developers using version 3.9.1 it is like this:
const pixelValue = (scale, v) => {
const val = scale.getValueForPixel(v)
return Math.trunc(isNaN(val) ? v * 6 : 3)
}
const $chart = document.getElementById("chart");
const chart = new Chart($chart, {
type: "bubble",
data: {
datasets: [{
label: "scaled radius",
backgroundColor: 'rgb(255, 99, 132)',
data: [{
x: 10,
y: 11,
size: 1
},
{
x: 20,
y: 5,
size: 3
}
],
radius: context => {
const scale = context.chart.scales.y
const size = context.dataset.data[context.dataIndex].size
const value = Math.abs(pixelValue(scale, size))
return value
},
}]
},
options: {
maintainAspectRatio: false,
scales: {
x: {
min: 0,
max: 30
},
y: {
min: 2,
max: 14
}
}
}
})
.wrapper {
max-width: 800px;
max-height: 180px;
}
canvas {
user-select: none;
}
<div class="wrapper" style="height: 180px; width: 600px">
<canvas id="chart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js" integrity="sha512-ElRFoEQdI5Ht6kZvyzXhYG9NqjtkmlkfYk0wr6wHxU9JEHakS7UJZNeml5ALk+8IKlU6jDgMabC3vkumRokgJA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Related

Trendline plugin brakes it all

I have a simple linear chart on my page thanks to chart.js library and it is working fine. Wanted to add a trendline to it. Im using both library and plugin directly in my html file.
<script src="https://cdn.jsdelivr.net/npm/chart.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline" type="text/javascript"></script>
But Im getting error Uncaught TypeError: Failed to execute 'createLinearGradient' on 'CanvasRenderingContext2D': The provided double value is non-finite.
I think, problem is somwhere with dataset. It is working as intended without trendline, but trendline breaks it.
HTML file contains:
<script src="js/progress.js" type="text/javascript"></script>
<input type="text" id="progress" name="progress" placeholder="0"
oninput="progress.validateValues()">
<canvas id="myChart"></canvas>
<script src="js/myChart.js" type="text/javascript"></script>
myChart.js file:
const ctx = document.getElementById('myChart');
const data = {
datasets: [{
label: 'Progress',
data: [],
fill: false,
borderColor: 'rgb(222, 11, 11)',
trendlineLinear: {
colorMin: 'red',
colorMax: 'green',
lineStyle: 'solid',
width: 2,
projection: true,
},
}],
};
chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
scales: {
x: {
type: 'linear',
min: 0,
suggestedMax: 6,
grid: {
display: true,
color: 'rgb(60, 80, 130)',
},
ticks: {
display: true,
color: 'rgb(120, 160, 255)',
backdropColor: 'rgba(0, 0, 0, 0)',
},
},
y: {
type: 'linear',
suggestedMin: 2,
suggestedMax: 8,
grid: {
display: true,
color: 'rgb(60, 80, 130)',
},
ticks: {
display: true,
color: 'rgb(120, 160, 255)',
backdropColor: 'rgba(0, 0, 0, 0)',
},
}
},
plugins: {
},
},
});
function pushNewData(chart, newData) {
chart.data.datasets.forEach((dataset) => {
dataset.data = newData;
});
chart.update();
};
and progress.js file:
const progress = {
xyDataSet: [],
validateValues: function () {
this.readValues();
pushNewData(chart, this.xyDataSet);
},
readValues: function () {
let arrayValues = [];
arrayValues = document.getElementById("progress").value.replace(/\s+/g, ' ').trim().split(' ').filter(function (element) {
return element >= 0 && element < 19;
});
for (let i = 0; i < arrayValues.length; i++) {
this.xyDataSet[i] = { x: i, y: arrayValues[i] }
};
},
};
In normal way user pass string of values with spaces between like 0 1 1 0 2 1 3 0 2 3 1. Im validating/clearing/parsing that string to array with coordinates newData = [{x: 0, y: 0},{x: 1, y: 1},{x: 2, y: 1},{x: 3, y: 0},{x: 4, y: 2}, etc] and updating chart.
Without trendline it works perfect. With trendline plugin - all gone.
Thank you for any tips!

Custom Apexchart

I am trying to create a graph with Apexcharts that looks like below. You can ignore the 2 vertical lines as they are not necessary.
Looking at the examples that apexcharts have, the closest thing I have found is Stacked Bar Charts. Maybe there is something better and I missed? If so please let me know.
https://apexcharts.com/javascript-chart-demos/bar-charts/stacked/
The way the chart below behaves is I get an array of data such as this:
[3, 7, 21, 55, 32, 3, 7, 58, 38] // and so on. You will notice the numbers can duplicate.
To simply things I will use Book and pages as a reference. The X axis represents number of pages and Y axis represents number of books. So by looking at the image I posted you can see there are 22 books that have 100 or less pages. There are 2 books with greater than 100 but less than 300 pages and 1 book with more than 300 and less than 400 pages.
I am having a real hard time figuring out how to configure the stacked bar chart to display things in this manner. I am not looking for an answer on how to manipulate my data, but more of how to configure the settings of the graph in order to get the correct display.
I have tried several configurations to no avail. Any help on this is most appreciated!
For reference to show work, some of things I have tried:
series: [{
name: 'Range 1',
data: [{ y: 44, x: 100 }, { y: 55, x: 100 }, { y: 41, x: 100 }, { y: 37, x: 100 }, { y: 22, x: 100 }, { y: 43, x: 100 }, { y: 21, x: 100 }]
}, {
name: 'Range 2',
data: [{ y: 44, x: 200 }, { y: 55, x: 200 }, { y: 41, x: 200 }, { y: 37, x: 200 }, { y: 22, x: 200 }, { y: 43, x: 200 }, { y: 21, x: 200 }]
}]
series: [{
name: 'Range 1',
data: [{ x: 44, y: 100 }, { x: 55, y: 100 }, { x: 41, y: 100 }, { x: 37, y: 100 }, { x: 22, y: 100 }, { x: 43, y: 100 }, { x: 21, y: 100 }]
}, {
name: 'Range 2',
data: [{ x: 44, y: 200 }, { x: 55, y: 200 }, { x: 41, y: 200 }, { x: 37, y: 200 }, { x: 22, y: 200 }, { x: 43, y: 200 }, { x: 21, y: 200 }]
}]
series: [{
name: 'Range 1',
data: [44, 55, 41, 37, 22, 43, 21]
}, {
name: 'Range 2',
data: [53, 32, 33, 52, 13, 43, 32]
}]
One of the largest issues I'm finding is that I can't seem to control values on the X axis or to keep the bars same width.
Here is an example of how it comes out from one of the attempts.
Lastly, the first image I showed above is generated from a Histogram from google charts. My requirements were to move all the google charts to apex charts so I'm trying to find something in apex charts equivalent to do the job.
There are two things that I do not really understand in your question: your data and why you want a stacked bar chart.
If you need to put the number of pages on your xaxis and the number of books on your yaxis, then a basic bar chart should be enough. I give you an example below:
let options = {
series: [{
name: 'Series',
data: [22, 2, 2, 1, 0, 0, 2, 0]
}],
chart: {
type: 'bar',
height: 350
},
dataLabels: {
enabled: false
},
xaxis: {
categories: [0, 100, 200, 300, 400, 500, 600, 700],
labels: {
offsetX: -13
},
title: {
text: 'Number of pages',
offsetY: 100
}
},
yaxis: {
title: {
text: 'Number of books'
}
},
tooltip: {
custom: ({series, seriesIndex, dataPointIndex, w}) => {
return `<div class="arrow_box"><div>Book(s): <strong>${series[seriesIndex][dataPointIndex]}</strong></div><div>Pages: <strong>${w.globals.labels[dataPointIndex]}-${w.globals.labels[dataPointIndex] + 100}</strong></div>`;
}
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
.arrow_box {
padding: 10px;
}
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
The trickiest part is the custom tooltip. I did this to put intervals in there instead of the category.
EDIT 1
Oops! I did not pay attention: my example above is not responsive (see labels). So maybe you could do something like that:
let options = {
series: [{
name: 'Series',
data: [22, 2, 2, 1, 0, 0, 2]
}],
chart: {
type: 'bar',
height: 350
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['0-100', '101-200', '201-300', '301-400', '401-500', '501-600', '601-700'],
title: {
text: 'Number of pages',
offsetY: 100
}
},
yaxis: {
title: {
text: 'Number of books'
}
},
responsive: [
{
breakpoint: 400,
options: {
xaxis: {
title: {
offsetY: 150
}
}
}
}
]
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="chart"></div>
This is not very elegant but I did not find a better solution.
EDIT 2
After reading your comment, I updated my example:
let options = {
series: [{
name: 'Sci-fi',
data: [44, 55, 41, 37, 22, 43, 21]
}, {
name: 'Fantasy',
data: [53, 32, 33, 52, 13, 43, 32]
}],
chart: {
type: 'bar',
height: 350,
stacked: true
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['0-100', '101-200', '201-300', '301-400', '401-500', '501-600', '601-700'],
title: {
text: 'Number of pages'
}
},
yaxis: {
title: {
text: 'Number of books'
}
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts#3.36.0"></script>
<div id="chart"></div>
EDIT 3
Using the analogy above, each stacked bar would show title of the book. As they are all different books.
If you want to do that, I think you will have to create one series for each book and just set one color to avoid getting an unreadable rainbow. But if you have a lot of books, you will most likely face performance issues.
You will find a better example below. It is not consistent because I randomized all series, but it seems closer to what you are trying to do.
function getAllSeries() {
let allSeries = [];
for (let i = 1; i <= 7; i++) {
let series = {};
series.name = `Book ${i}`;
series.data = [];
for (let j = 0; j < 7; j++) {
if ((Math.random() * 10) < 5) {
series.data.push(1);
} else {
series.data.push(null);
}
}
allSeries.push(series);
}
return allSeries;
}
let options = {
series: getAllSeries(),
chart: {
type: 'bar',
height: 350,
stacked: true
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['0-100', '101-200', '201-300', '301-400', '401-500', '501-600', '601-700'],
title: {
text: 'Number of pages'
}
},
yaxis: {
title: {
text: 'Number of books'
}
},
legend: {
show: false
},
colors: ['#269ffb'],
stroke: {
colors: ['#ffffff'],
width: 1
},
tooltip: {
custom: ({series, seriesIndex, dataPointIndex, w}) => {
return `<div class="arrow_box"><div>Name: <strong>${w.config.series[seriesIndex].name}</strong></div><div>Pages: <strong>${w.globals.labels[dataPointIndex]}</strong></div>`;
}
}
};
let chart = new ApexCharts(document.querySelector('#chart'), options);
chart.render();
.arrow_box {
padding: 10px;
}
<script src="https://cdn.jsdelivr.net/npm/apexcharts#3.36.0"></script>
<div id="chart"></div>

Chart.js issue in plotting numeric X and Y in line chart

I have bellow Code to display a Chart using Chart.js.
<canvas id="canvasline" width="200" height="200"></canvas>
<script>
var ctx = document.getElementById('canvasline').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['0', '30', '50'],
datasets: [{
data: [
{ x: "0", y: 0 },
{ x: "10", y: 10 },
{ x: "20", y: 20 },
{ x: "30", y: 30 },
{ x: "40", y: 40 },
{ x: "50", y: 50 },
],
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
display: true,
suggestedMin: 0,
suggestedMax: 100,
maxTicksLimit: 10
},
gridLines: {
display: true
}
}],
xAxes: [{
ticks: {
display: true,
suggestedMin: 0,
suggestedMax: 100,
maxTicksLimit: 3
},
gridLines: {
display: true
}
}]
}
}
});
</script>
Working Code Example:
https://jsfiddle.net/2vcrsq6n/
I am facing the below issues:
For the X-Axis label "30", I see X data "10" is getting displayed
At the runtime I get an JSON array with X and Y values as an array, I want to plot this X and Y numbers on the graph how should I implement it.
When the data is specified as individual points through objects that contain an x and an y property, you should not define data.labels.
Also make sure, the x and y values are numbers but not strings.
Please take a look at your amended code below that uses the most recent stable version of Chart.js (2.9.3).
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'scatter',
data: {
datasets: [{
data: [
{ x: 0, y: 0 },
{ x: 10, y: 10 },
{ x: 20, y: 20 },
{ x: 30, y: 30 },
{ x: 40, y: 40 },
{ x: 50, y: 50 }
],
showLine: true,
fill: false,
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 100,
maxTicksLimit: 10
}
}],
xAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 100,
maxTicksLimit: 3
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart"></canvas>
To update your chart with new data, simply add or replace the data in the dataset and invoke chart.update() afterwards.
myChart.data.datasets[0].data = <new data>;
myChart.update();

Chart.js How to set line height only to the points?

How to set line height only until points? I have used 'gridLines: display:false' but it hides all lines. Here on image below how it should be
It's not a native option in Chart.js but you can implement it yourself via plugin. See the annotations in the below code.
new Chart(document.getElementById('chart'), {
type: 'line',
data: {
labels: [0, 1, 2, 3, 4, 5],
datasets: [{
label: 'series 1',
data: [0, 2, 4, 3, 1, 0]
}]
},
options: {
maintainAspectRatio: false,
scales: {
xAxes: [{
gridLines: {
display: false, // must be false since we're going to draw our own 'gridlines'!
color: 'rgba(255, 0, 0, .2)', // can still set the colour.
lineWidth: 5 // can still set the width.
}
}],
yAxes: [{
gridLines: {
display: false
},
ticks: {
beginAtZero: true
}
}]
}
},
plugins: [{ // this is the magical bit :)
afterRender: function(c, options) {
let meta = c.getDatasetMeta(0),
max;
c.ctx.save();
c.ctx.strokeStyle = c.config.options.scales.xAxes[0].gridLines.color;
c.ctx.lineWidth = c.config.options.scales.xAxes[0].gridLines.lineWidth;
c.ctx.beginPath();
meta.data.forEach(function(e) {
if (max == undefined || c.config.data.datasets[0].data[e._index] > max) {
max = c.config.data.datasets[0].data[e._index];
}
c.ctx.moveTo(e._model.x, meta.dataset._scale.bottom);
c.ctx.lineTo(e._model.x, e._model.y);
});
c.ctx.textBaseline = 'top';
c.ctx.textAlign = 'right';
c.ctx.fillStyle = 'black';
c.ctx.fillText('Max value: ' + max, c.width - 10, 10);
c.ctx.stroke();
c.ctx.restore();
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="chart"></canvas>

Do not displaying first chart point chartjs

I'm using chartjs lib to draw charts.
And I want to draw some simple, linear chart, but when do it some how the first point of the chart isn't shown correctly :
https://scr.hu/ALkPXP
It happends when the lowest data point x value have the same value as the minimum xAxes.
When I decrease minimum ax value it works correcty:
https://scr.hu/Gw4Lpy
My whole chart looks like :
const scatterChart2 = new Chart(this.canvas.nativeElement,
{
'type': 'line',
'data':
{
'datasets': [
{
'label': 'Scatter Dataset',
'data': [
{
x: 1,
y: 0,
}, {
x: 65,
y: 20,
}, {
x: 30,
y: 22,
}, {
x: 44,
y: 55,
}],
'fill': false,
'borderColor': 'rgb(75, 192, 192)'
}]
},
'options': {
scales: {
xAxes: [{
ticks: {
min: 1,
stepSize: 1
},
offset: true,
type: 'linear',
position: 'bottom'
}]
},
'showLines': true
}
});
}
And in HTML my Canvas looks like :
<div style="max-width: 1000px;max-height: 1000px;">
<canvas #canvas id="myChart" ></canvas>
</div>
How should I show first point correctly ?

Categories

Resources