Related
I am currently trying to use the plugin chartjs-plugin-annotation in my react project.
Unfortunately, it is not working...
He is my implementation :
import React, { Component } from "react";
//import "./css/tideComponent.css";
import jsonData from "./ressources/tideOostende2023.json";
import "chart.js/auto";
import { Chart } from "react-chartjs-2";
import * as ChartAnnotation from "chartjs-plugin-annotation";
class Tide extends Component {
state = {
dayDate: new Date().toJSON().slice(5, 10),
highTide: "",
highTide2: "",
lowTide: "",
lowTide2: "",
};
async componentDidMount() {
const index = jsonData.findIndex(
(item) => item.date === this.state.dayDate
);
//TODO store tide in an array(using split method) & filter low to high to have a correct graph
this.setState({
highTide: jsonData[index].highTide,
highTide2: jsonData[index].highTide2,
lowTide: jsonData[index].lowTide,
lowTide2: jsonData[index].lowTide2,
});
}
timeToNumeric(tideTime) {
const tideTimeSplitted = tideTime.split(":");
return tideTimeSplitted[0] * 1 + tideTimeSplitted[1] / 60;
}
handleTideData() {
if (
this.timeToNumeric(this.state.highTide) <
this.timeToNumeric(this.state.lowTide)
)
return [
{ x: -2, y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide2), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide2), y: 0.5 },
{ x: 26, y: 1.5 },
];
return [
{ x: -2, y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide), y: 1.5 },
{ x: this.timeToNumeric(this.state.lowTide2), y: 0.5 },
{ x: this.timeToNumeric(this.state.highTide2), y: 1.5 },
{ x: 26, y: 0.5 },
];
}
render() {
const data = {
datasets: [
{
data: this.handleTideData(),
fill: false,
backgroundColor: "rgb(35, 71, 89, 0.88)",
borderColor: " rgb(35, 71, 79, 0.88)",
tension: 0.4,
},
],
};
const options = {
annotation: {
annotations: [
{
type: "line",
mode: "horizontal",
scaleID: "x",
value: 1,
borderColor: "white",
borderWidth: 2,
},
],
},
scales: {
x: { min: 0, max: 24, ticks: { stepSize: 1 } },
y: { min: 0, max: 2.2, display: false },
},
showLine: true,
pointStyle: false,
plugins: {
legend: { display: false },
},
};
return (
<div className="tideContainer">
<Chart
type="scatter"
data={data}
options={options}
plugins={ChartAnnotation}
/>
</div>
);
}
}
export default Tide;`
I tried different things but still not working. I also reviewed multiple question on SO but cannot find my solution. Chart Js is correctly working with my implementation, it is only the plugin that does not work.
Thank you in advance for your great help !!!
I think plugins property in react-chartjs-2 should be an array, I guess.
<Chart
type="scatter"
data={data}
options={options}
plugins={[ChartAnnotation]}
/>
The options config for annotation plugin is not in the right node.
It must be added in options.plugins node.
const options = {
plugins: { // <-- to add, was missing
annotation: {
annotations: [
{
type: "line",
mode: "horizontal",
scaleID: "x",
value: 1,
borderColor: "white",
borderWidth: 2,
},
],
},
}
chart
const data = {
labels: Array(coordinates.length).fill("l"),
datasets: buildDataset(),
options: {
animation: false,
scales: {
// ???????
},
legend: {
display: false
},
tooltips: {
callbacks: {
label: function(tooltipItem) {
return tooltipItem.yLabel;
}
}
},
maintainAspectRatio: false,
scales: {
myScale: {
position: 'left',
}
},
elements: {
point:{
radius: 0
}
}
}
}
return (
<Chart>
<Line
data={data}
width={50}
height={20}
options={data.options}>
</Line>
</Chart>
)
// ~~~~~
let obj = {
label: stops[0].miles == 0 ? index : index + 1,
data: points,
backgroundColor: colors[index],
tension: 0.4,
fill: true
}
These charts are built from an array of obj objects. The points variable that data refers is an array of object like: [{x: 0, y: 10257}, {x: 1, y: 10245}]
How do I get my line chart to display these different datasets side by side? I assume it has something to do with the scales parameter but wasn't able to find anything that worked in the docs.
Thanks!
For the object notation to work chart.js needs values to plot them against (not the index in the array) so you cant just provide an array containing only the value l.
You can either provide a labels array containing increasing numbers to which you match it or remove it and set your x scale to linear.
Labels example:
var options = {
type: 'line',
data: {
labels: [0, 1, 2, 3],
datasets: [{
label: '# of Votes',
data: [{
x: 0,
y: 4
}, {
x: 1,
y: 6
}, {
x: 2,
y: 2
}],
borderColor: 'pink'
},
{
label: '# of Points',
data: [{
x: 2,
y: 2
}, {
x: 3,
y: 3
}],
borderColor: 'blue'
}
]
},
options: {
scales: {}
}
}
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.5.0/chart.js"></script>
</body>
Linear example:
var options = {
type: 'line',
data: {
datasets: [{
label: '# of Votes',
data: [{
x: 0,
y: 4
}, {
x: 1,
y: 6
}, {
x: 2,
y: 2
}],
borderColor: 'pink'
},
{
label: '# of Points',
data: [{
x: 2,
y: 2
}, {
x: 3,
y: 3
}],
borderColor: 'blue'
}
]
},
options: {
scales: {
x: {
type: 'linear'
}
}
}
}
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.5.0/chart.js"></script>
</body>
I made a custom chartjs component in reactjs and want to render dates in xAxes and numbers from -1 to 1 in yAxes but it renders data not in a proper way.
import React, { useRef, useEffect } from "react";
import Chart from "chart.js";
const ChartComponent = ({ data, label, min, max }) => {
const canvasRef = useRef(null);
useEffect(() => {
const canvasObj = canvasRef.current;
const context = canvasObj.getContext("2d");
new Chart(context, {
type: "line",
data: {
datasets: [
{
label: label,
backgroundColor: "transparent",
data: data,
},
],
},
options: {
scales: {
xAxes: [
{
type: "time",
distribution: "linear",
time: {
unit: "month",
displayFormats: {
quarter: "YYYY mm dd",
},
},
},
],
yAxes: [
{
ticks: {
suggestedMax: max,
suggestedMin: min,
},
},
],
},
},
});
}, [data, label, min, max]);
return <canvas ref={canvasRef}> </canvas>;
};
export default ChartComponent;
and I'm passing the data like this to the component
<ChartComponent
min={-1}
max={1}
label="date"
data={[
{
x: "30/03/2018",
y: 0.1158,
},
{
x: "24/09/2018",
y: 0.1975,
},
{
x: "23/12/2018",
y: 0.1913,
},
{
x: "23/03/2019",
y: 0.2137,
},
]}
/>;
I have to metion that I have done the same thing and that is working alright this is example below
<ChartComponent
min={1270}
max={1272}
label=" مساحت دریاچه"
data={[
{
x: "04/02/2017",
y: 1270.7,
},
{
x: "06/26/2017",
y: 1270.74,
},
{
x: "09/19/2017",
y: 1270.31,
},
{
x: "12/18/2017",
y: 1270.28,
},
{
x: "06/16/2018",
y: 1270.81,
},
{
x: "09/24/2018",
y: 1270.27,
},
{
x: "12/23/2018",
y: 1270.54,
},
{
x: "05/25/2019",
y: 1271.94,
},
{
x: "06/18/2019",
y: 1271.84,
},
{
x: "09/19/2019",
y: 1271.31,
},
{
x: "12/18/2019",
y: 1271.25,
},
{
x: "03/12/2020",
y: 1271.48,
},
{
x: "06/25/2020",
y: 1271.72,
},
]}
/>;
any recommendation would be appreciated. thank you.
The problem is that the date strings you provide are not of ISO 08601 nor RFC 2822 Date time format, hence they cannot be parsed by Moment.js, which is internally used by Chart.js.
To make it work, you have to define xAxes.time.parser: "DD.MM.YYYY".
Please take a look at below runnable code and see how it works. This is pure JavaScript example but it should also work with react-chartjs-2.
new Chart("myChart", {
type: "line",
data: {
datasets: [{
label: 'My Dataset',
data: [
{ x: "30/03/2018", y: 0.1158 },
{ x: "24/09/2018", y: -0.1975 },
{ x: "23/12/2018", y: 0.1913 },
{ x: "23/03/2019", y: -0.2137 }
],
fill: false
}]
},
options: {
scales: {
xAxes: [{
type: "time",
time: {
parser: "DD.MM.YYYY",
unit: "month",
displayFormats: {
month: "YYYY MM DD",
}
}
}],
yAxes: [{
ticks: {
suggestedMax: 1,
suggestedMin: -1
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<canvas id="myChart" height="100"></canvas>
As I already mentioned, Chart.js internally uses Moment.js for the functionality of the time axis. Therefore make sure to use the bundled version of Chart.js that includes Moment.js in a single file.
I have figured out how to set the size for an annotation individually:
labels: [
{
point: {
x: chart.xAxis[0].max - 0.1,
y: 50,
xAxis: 0,
yAxis: 0
},
height: 100,
shape: "rect",
text: "test",
verticalAlign: "bottom"
}
]
I would like it to position between 50 and 150. How can I achieve that?
And additionally, I would like the text to be aligned in the center of the box?!
You can calculate the height by using toPixels method. You have to also take a padding into account. To center the text you can use asynchronous function, because annotations do not exist in load event yet.
chart: {
events: {
load: function() {
const chart = this;
const height =
chart.yAxis[0].toPixels(50) - chart.yAxis[0].toPixels(150);
chart.addAnnotation({
labels: [
{
point: {
x: chart.xAxis[0].max - 0.1,
y: 150,
xAxis: 0,
yAxis: 0
},
y: 0,
padding: 0,
height: height,
shape: "rect",
text: "test",
verticalAlign: "top"
}
]
});
}
}
}
Live demo: https://codesandbox.io/s/qqqkx2zr49
API: https://api.highcharts.com/class-reference/Highcharts.Axis#toPixels
I'm using multichart with line and scatterplot. The line chart works fine but the scatterplot data does not plot correctly for the x-axis data.
Can someone please provide a working example other than the one on github? Here is the selections:
$scope.options = {
chart: {
type: 'multiChart',
height: 450,
margin: {
top: 30,
right: 60,
bottom: 50,
left: 70
},
color: d3.scale.category10().range(),
//useInteractiveGuideline: true,
duration: 500,
xAxis: {
ticks: 10,
tickFormat: function (d) {
return d3.format(',10d')(d);
}
},
yAxis1: {
ticks: 10,
tickFormat: function (d) {
return d3.format('10d')(d);
}
}
}
};
function generateData() {
var data1 = [{ x: 0, y: 25 }, { x: 25, y: 25 }, { x: 25, y: 0 }];
var data2 = [{ x: 0, y: 50 }, { x: 50, y: 50 }, { x: 50, y: 0 }];
var data3 = [{ x: 0, y: 75 }, { x: 75, y: 75 }, { x: 75, y: 0 }];
var data4 = [{ x: 0, y: 100 }, { x: 100, y: 100 }, { x: 100, y: 0 }];
var scatter = [{ x: 10, y: 30, size: Math.random(), shape: 'circle' }, { x: 20, y: 50, size: Math.random(), shape: 'circle' }, { x: 30, y: 80, size: Math.random(), shape: 'circle' }];
var testdata = [];
testdata.push({ key: 'Stream1', values: data1 });
testdata.push({ key: 'Stream2', values: data2 });
testdata.push({ key: 'Stream3', values: data3 });
testdata.push({ key: 'Stream4', values: data4 });
testdata.push({ key: 'Stream5', values: scatter });
testdata[0].type = 'line';
testdata[0].yAxis = 1;
testdata[1].type = 'line';
testdata[1].yAxis = 1;
testdata[2].type = 'line';
testdata[2].yAxis = 1;
testdata[3].type = 'line';
testdata[3].yAxis = 1;
testdata[4].type = 'scatter';
testdata[4].yAxis = 1;
return testdata;
}
I also encounter this problem, and my workaround is to add a dummy scatter series with the start and end point only, and the series color is transparent.
For example:
line series: 1,2,3,4,5...100
scatter 1: 5, 67, 83
dummy scatter: 1, 100
Then the x-domain of the scatter will scale with the line series