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!
Related
I have a radar chart in which "100" represents the global average value for a characteristic.
To make the chart more readable I want to make only the grid line that indicates the 100 value mark dashed.
Is there a way to do this?
Current chart code is here: jsfiddle.
const config = {
type: 'radar',
data: data,
options: {
elements: {
line: {
borderWidth: 3
},
point: {
pointRadius: 5
}
},
scales: {
r: {
angleLines: {
lineWidth: 2
},
grid: {
circular: true,
lineWidth: 2
}
}
}
}
};
In my opinion, the logically correct answer is to define grid.borderDash as follows:
grid: {
...
borderDash: ctx => ctx.tick.value == 100 ? [8, 4] : []
},
To make it work however, I had to use ctx.tick.value == 95 (This could be a bug in Chart.js). Please take a look at your amended code and see how it works.
let dataValues = [100, 120, 80, 100, 90, 110, 100, 100, 100]
const sum = dataValues.reduce((a, b) => a + b, 0);
const avg = sum / dataValues.length;
const sorted = [...dataValues].sort(function(a, b) {
return a - b;
});
const data = {
labels: dataValues.map((v, i) => 'Signal ' + (i + 1)),
datasets: [{
label: '9 signals',
data: dataValues,
fill: true,
backgroundColor: 'rgba(210, 203, 203, 0.4)',
borderColor: 'rgb(210, 203, 203, 0.6)',
pointBackgroundColor: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value < sorted[3] ? 'blue' :
value < sorted[5] ? 'green' :
value < sorted[7] ? 'orange' :
'red';
},
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgb(255, 99, 132)'
}
]
};
const config = {
type: 'radar',
data: data,
options: {
elements: {
line: {
borderWidth: 3
},
point: {
pointRadius: 5
}
},
scales: {
r: {
suggestedMin: 70,
angleLines: {
lineWidth: 2
},
grid: {
circular: true,
lineWidth: 2,
borderDash: ctx => ctx.tick.value == 95 ? [8, 4] : []
},
ticks: {
stepSize: 5
}
}
}
}
};
let myChart = new Chart('myChart',
config
);
.chartBox {
width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<div class="chartBox">
<canvas id="myChart"></canvas>
</div>
I have an HTML page with a charts.js chart that works. I'm trying to set the minimum and maximum values of the chart, but I can't set the minimum and maximum values. I searched different posts but they all say the same, and I'm pretty sure I'm already doing that...
> scales: {
> yAxes: [{
> ticks: {
> min: -100,
> max: 100
> }
> }]
> }
I just cant figure out what's wrong.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.0.0/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0-rc/dist/chartjs-plugin-datalabels.min.js"></script>
<title>daily</title>
</head>
<body>
<canvas class='temp_chart' id='temp_chart'>
</body>
<script>
function square_data(chart){
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "#FFA500";
ctx.rect(145, 70, 15, 15);
ctx.fill()
ctx.fillStyle = "#fff";
ctx.fillText(chart.dataset.data[chart.dataIndex], 147,82, 10);
ctx.stroke();
return c
}
const time_of_day = ['Morning', 'Day', 'Evening', 'Night']
var temperatures = [15, 18, 4, -6]
var ctx = document.getElementById('temp_chart').getContext('2d')
window.chart = new Chart(ctx, {
type: 'line',
data: {
labels: time_of_day,
datasets: [{
fill:false,
label: 'Cantidad Estudiantes',
data: temperatures,
borderColor: [
'rgba(0, 0, 0, 1)',
],
borderWidth: 3
}]
},
options: {
plugins: {
legend: {
display: false,
},
datalabels: {
anchor: 'start',
align: '-45',
clamp: true,
color: "orange",
}
},
elements:{
"point":{"pointStyle":square_data},
}
},
scales: {
yAxes: [{
ticks: {
min: -100,
max: 100
}
}]
}
});
</script>
That is because you are using v2 syntax with v3, for all changes please read the migration guide and you defined the scale config outside of the options object.
In v3 all scales are an object where the key of the object is the ID of the scale
Also for the datalabels plugin to work you need to register it, see first line in the script block.
Last thing dont include 2 major versions of a lib, high chance of giving errors
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.0.0/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<title>daily</title>
</head>
<body>
<canvas class='temp_chart' id='temp_chart'>
</body>
<script>
Chart.register(ChartDataLabels); // Added this line
function square_data(chart) {
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "#FFA500";
ctx.rect(145, 70, 15, 15);
ctx.fill()
ctx.fillStyle = "#fff";
ctx.fillText(chart.dataset.data[chart.dataIndex], 147, 82, 10);
ctx.stroke();
return c
}
const time_of_day = ['Morning', 'Day', 'Evening', 'Night']
var temperatures = [15, 18, 4, -6]
var ctx = document.getElementById('temp_chart').getContext('2d')
window.chart = new Chart(ctx, {
type: 'line',
data: {
labels: time_of_day,
datasets: [{
fill: false,
label: 'Cantidad Estudiantes',
data: temperatures,
borderColor: [
'rgba(0, 0, 0, 1)',
],
borderWidth: 3
}]
},
options: {
// Changed this block
scales: {
y: {
min: -100,
max: 100,
}
},
plugins: {
legend: {
display: false,
},
datalabels: {
anchor: 'start',
align: '-45',
clamp: true,
color: "orange",
}
},
elements: {
"point": {
"pointStyle": square_data
},
}
},
});
</script>
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>
I want to create a circular band inside a scatterpolar chart as follows:
For drawing band, I want to give two inputs: R_inner and R_outer values from radial axis so that I can modify band's thickness.
In the current image, I have plotted the band using marker with size. I have given the HTML,JS code as follows:
function getrandom(num, mul) {
var value = []
for (i = 0; i <= num; i++) {
rand = Math.random() * mul;
value.push(rand);
}
return value;
};
function get_update_data() {
var data = [{
type: "scatterpolargl",
r: [50],
theta: [0],
mode: "markers",
name: "Wall Radius",
marker: {
color: "green",
size: 10,
line: {
color: "white"
},
opacity: 1
},
cliponaxis: false
},
{
type: "scatterpolargl",
r: [0],
theta: [0],
mode: "markers",
name: "Band",
marker: {
symbol: "circle-open",
color: "red",
"sizemode": "diameter",
size: 200,
line: {
width: 30
},
opacity: 0.3
},
cliponaxis: true
},
];
return data;
};
var layout = {
title: "",
font: {
size: 15
},
polar: {
bgcolor: "rgb(255, 255, 255)",
angularaxis: {
tickwidth: 2,
linewidth: 3,
color: "rgb(0,153,204)",
layer: "below traces"
},
radialaxis: {
side: "counterclockwise",
showline: false,
tickwidth: 0,
showgrid: true,
color: "rgb(0,153,204)",
ticks: "",
showticklabels: true,
}
},
paper_bgcolor: "rgb(255,255,255)",
autosize: false,
width: 400,
height: 400,
margin: {
l: 10,
r: 10,
b: 50,
t: 50,
pad: 4
},
};
function get_update_layout() {
return layout;
};
Plotly.newPlot('myDiv', get_update_data(), layout);
<head>
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<div id="myDiv">
<!-- Plotly chart will be drawn inside this DIV -->
</div>
<script>
<!-- JAVASCRIPT CODE GOES HERE -->
</script>
</body>
I am trying to position a label related to the dataset below a point on a bubble graph and to have the value displayed on top of the point.
I have tried returning two different values, but it only allows me to return one.
Because of this I have returned both values within the same string.
Is it possible to return the label and position it below the value of the dataset and to put the value above the point of the dataset?
If it isn't possible would it be possible to somehow get the text to align to the center after the line break I have added to the string?
Here is what I have so far
This is what I am trying to achieve, but with the value above the bubble
Here is my code so far:
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [
{
label: 'Top',
data: [
{x: 110, y: 0, r: 12, name: "Performance"}
],
backgroundColor: "rgba(255,255,255,1)",
borderColor: "rgba(91,182,209,1)",
borderWidth: 2,
hoverBackgroundColor: "rgba(255,255,255,1)",
hoverBorderWidth : 2
},
{
label: 'Average',
data: [
{x: 75, y: 0, r: 12, name: "Performance"}
],
backgroundColor: "rgba(255,255,255,1)",
borderColor: "rgba(91,182,209,1)",
borderWidth: 2,
hoverBackgroundColor: "rgba(255,255,255,1)",
hoverBorderWidth : 2
},
{
label: 'You 2017',
data: [
{x: 55, y: 0, r: 15, name: "Performance"}
],
backgroundColor: "rgba(255,255,255,1)",
borderColor: "rgba(91,182,209,1)",
borderWidth: 2,
hoverBackgroundColor : "rgba(255,255,255,1)",
hoverBorderWidth : 2
},
{
label: 'You 2018',
data: [
{x: 90, y: 0, r: 15, name: "Performance"} // The labe needs to be
],
backgroundColor: "rgba(255,255,255,1)",
borderColor: "rgba(91,182,209,1)",
borderWidth: 2,
hoverBackgroundColor : "rgba(255,255,255,1)",
hoverBorderWidth : 2
}
]
},
options: {
legend: {
display: false
},
plugins: {
datalabels: {
anchor: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? 'end' : 'center';
},
align: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? 'end' : 'center';
},
color: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? context.dataset.borderColor : 'white';
},
font: {
weight: 'bold'
},
formatter: function (value, context) {
ctx.textAlign = "center";
return context.dataset.label + "\n" + Math.round(value.x);
},
offset: 2,
padding: 0
}
},
maintainAspectRatio: false,
scales: {
yAxes: [{
id: 'first-y-axis',
type: 'linear',
ticks: {
min: 0,
max: 1,
stepSize: 1,
display: false
},
gridLines: {
display: false,
drawBorder: false
}
}],
xAxes: [
{
id: 'first-x-axis',
ticks: {
min: 50, // Controls where axis starts
max: 120, // Controls where axis finishes
stepSize: 70 // Control the gap between the first x-axis value and the last x-axis value
},
gridLines: {
display: false,
lineWidth: 3 // Width of bottom line
}
}
]
}
}
});
Any help or suggestions would be greatly appreciated ^_^
I figured this out by applying 'textAlign' within the charts parameters and set it to centre.
You can see this in the code below:
plugins: {
datalabels: {
anchor: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? 'end' : 'center';
},
align: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? 'end' : 'center';
},
color: function (context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 1000 ? context.dataset.borderColor : 'white';
},
textAlign: 'center',
font: {
weight: 'bold',
alignment: 'right' // Is this the place to be doing this?
},
formatter: function (value, context) {
return context.dataset.label + '\n' + Math.round(value.x);
},
offset: 2,
padding: 0
}
},
Hope this helps ^^