I want to change options of HighChart by useState.
Then I could use a button click to re-render the chart.
Here is my code:
const options = {
title: {
text: "My chart",
},
series: [
{
data: [1, 2, 3, 7, 2, 3, 1, 0],
},
{
data: [1, 2, 1, 7, 3, 3, 3, 0],
},
],
};
export default function HighChartContainer() {
const [chartOptions, setChartOptions] = useState(options);
const handleChangeOptions = () => {
const newLineData = [
{
data: [1, 2, 1, 4, 2, 3, 7, 0],
},
{
data: [1, 2, 1, 6, 3, 5, 2, 0],
},
];
const newOptions = { ...chartOptions, series: newLineData };
setChartOptions(newOptions);
};
return (
<div>
<ChartWrapper>
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
/>
</ChartWrapper>
<div>
<button type="button" onClick={handleChangeOptions}>
Click me
</button>
</div>
</div>
);
}
It didn't work. I tried google but couldn't find anyone who wrote code in this pattern.
Is there anything wrong?
My code sandbox:
https://codesandbox.io/s/serene-framework-jjoh4?file=/src/App.js
You need to remove handleChangeOptions from the callback to see the effect. Additionally it is enough to update the chart with only new options:
const handleChangeOptions = () => {
const newLineData = [
{
data: [...]
},
{
data: [...]
}
];
const newOptions = { series: newLineData };
setChartOptions(newOptions);
};
Live demo: https://codesandbox.io/s/angry-johnson-gpv99?file=/src/App.js
Docs: https://github.com/highcharts/highcharts-react#optimal-way-to-update
After you update question. I saw the problem is you pass callback={handleChangeOptions} in HighchartsReact. So you just remove it. everything will work normally.
I am new to Three.js and
I have found a few answers on the topic but none have used buffer geometry. I am making a 3D terrain project of Europe on React and I want to be able to change the water levels depending on some values. In order to do that my coloring has to be done vertex by vertex instead of adding a texture from an image where the coloring will not change if I change the vertices' values. So, depending on the "height" of the vertex I want to assign different colors.
Here's my componentDidMount function:
const SIZE_AMPLIFIER = 15;
var container = document.getElementById("main_map");
var scene, camera, renderer, controls;
var data, plane;
//load map data from bin file
function loadTerrain(file) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', file, true);
xhr.onload = function (evt) {
if (xhr.response) {
data = new Uint16Array(xhr.response)
init()
}
};
xhr.send(null);
}
loadTerrain('stats.bin');
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(10000, 4000, 0);
// initialize scene
scene = new THREE.Scene();
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.append(renderer.domElement);
//initialize controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 1;
controls.rotateSpeed = .8;
controls.maxPolarAngle = Math.PI / 2 - .3;
// initialize plane
plane = new THREE.PlaneBufferGeometry(1500 * SIZE_AMPLIFIER, 1500 * SIZE_AMPLIFIER, 999 , 999);
plane.castShadow = true;
plane.receiveShadow = true;
var vertices = plane.attributes.position.array;
console.log(data)
// apply height map to vertices of plane
// assign different color depending on the value??
for (let i = 0, j = 2; i < data.length; i += 1, j += 3) {
if(data[i] == 0){
vertices[j] = 0
}else {
vertices[j] = data[i] / 65535 * 325 + 10
}
}
// Add texture image
var material = new THREE.MeshLambertMaterial({
map:THREE.ImageUtils.loadTexture('stats_color.png')
})
var mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = - Math.PI / 2;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
plane.computeFaceNormals();
plane.computeVertexNormals();
scene.add(mesh);
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
//controls.update();
}
The result after this (it is satisfying for me but unfortunately not effective for the purpose of the application):
Here is an article on how to use BufferGeometry
To add colors you make a Float32Array of colors and add it as an attribute called 'color'.
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), uvNumComponents));
You then set material.vertexColors to true;
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 3;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
// NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
// Only trying to make it clear most vertices are unique
const vertices = [
// front
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
// right
{ pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
// back
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
// left
{ pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 0], },
// top
{ pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 0], },
// bottom
{ pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 0], },
];
const positions = [];
const normals = [];
const uvs = [];
const colors = [];
for (const vertex of vertices) {
positions.push(...vertex.pos);
normals.push(...vertex.norm);
uvs.push(...vertex.uv);
colors.push(Math.random(), Math.random(), Math.random());
}
const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
const uvNumComponents = 2;
const colorNumComponents = 3;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), colorNumComponents));
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
I have created a areaspline chart using highcharts library and making it animated with play/pause button transition between weeks of data
Ref. https://www.highcharts.com/blog/tutorials/176-charts-in-motion/
jsfiddle Ref. https://jsfiddle.net/larsac07/wkev75nL/?utm_source=website&utm_medium=embed&utm_campaign=wkev75nL
in the above example, we are animating the chart week wise
dataSet used :
dataSequence = [
{
name: 'Week 1',
data: [1, 2, 2, 1, 1, 2, 2]
}, {
name: 'Week 2',
data: [6, 12, 2, 3, 3, 2, 2]
}, {
name: 'Week 3',
data: [4, 5, 6, 5, 5, 4, 9]
}, {
name: 'Week 4',
data: [5, 5, 6, 6, 5, 6, 6]
}, {
name: 'Week 5',
data: [6, 7, 7, 6, 6, 6, 7]
}, {
name: 'Week 6',
data: [8, 9, 9, 8, 8, 8, 9]
}, {
name: 'Week 7',
data: [9, 10, 4, 10, 9, 9, 9]
}, {
name: 'Week 8',
data: [1, 10, 10, 10, 10, 11, 11]
}, {
name: 'Week 9',
data: [11, 11, 12, 12, 12, 11, 11]
}
]
I don't want change chart
on play button click I want animate the data points 11 data point for week1 to same data point but different value on y axis for week2
xAxis = ["week1", "Week2", ..... ],
yAxis = [[1,2,3,4,5,6,7,8,9,10,11], [3,5,7,8,2,1,5,7,6,1,10], ....]
on the play button it would transit between week1 then will go to week 2 and so till last week number available.
Trying to have something like this Ref. https://aatishb.com/covidtrends/
this chart is plotted using this dataset for series
Highcharts.chart("container", {
chart: {
type: "areaspline"
},
tooltip: {
shared: true,
valueSuffix: " units"
},
xAxis: {
categories: [
"Week 1",
"Week 2",
"Week 3",
"Week 4",
"Week 5",
"Week 6",
"Week 7"
]
},
yAxis: {
title: {
text: "Index"
}
},
legend: {
layout: "horizontal",
align: "right",
verticalAlign: "top",
x: 50,
y: 50,
floating: true,
borderWidth: 1,
backgroundColor:
(Highcharts.theme && Highcharts.theme.legendBackgroundColor) ||
"#FFFFFF"
},
plotOptions: {
areaspline: {
fillOpacity: 0.5
}
},
credits: {
enabled: false
},
series: [
{
name: "By week",
data: dataSequence[value].data.slice()
},
{
type: "spline",
name: "Topic 1",
data: [3, 2, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 2",
data: [1, 5, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 3",
data: [3, 7, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 4",
data: [5, 1, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 5",
data: [7, 3, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 6",
data: [9, 2, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 7",
data: [11, 8, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 8",
data: [13, 11, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 9",
data: [15, 7, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 10",
data: [7, 5, 1, 3, 4, 7, 8]
}
],
title: {
text: ""
},
subtitle: {
text: "Efficiency Index of Topics"
}
});
this my update function in react
update(increment) {
var input = $("#play-range")[0];
var output = $("#play-output")[0];
if (increment) {
input.value = parseInt(input.value) + increment;
}
output.innerHTML = this.state.dataSequence[input.value].name;
this.setState({
value: input.value
});
if (input.value >= input.max) {
// Auto-pause
this.pause();
this.setState(
{
value: 0
},
() => {
output.innerHTML = this.state.dataSequence[0].name;
}
);
}
}
the whole chart is plotted at once, I need something that it should transit
first, it plots all data points for week1 then week 2 after that week 3 when I click on the play button
You need to start with an empty data and use addPoint method in the update function:
function update(increment) {
var input = $('#play-range')[0],
output = $('#play-output')[0],
increment;
chart.series[0].addPoint(dataSequence[input.value].data[actualPointIndex]);
actualPointIndex += increment;
if (actualPointIndex === 6) {
actualPointIndex = 0;
input.value = parseInt(input.value) + increment;
}
output.innerHTML = dataSequence[input.value].name; // Output value
if (input.value >= input.max) { // Auto-pause
pause($('#play-pause-button')[0]);
}
}
Live demo: https://jsfiddle.net/BlackLabel/stpxyfca/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint
I've built a bubble chart on quadrant graph using google-charts in reactjs but I can only had two labels on my axis and I need four.
I've tried fiddling with the code but unsuccessfully. I don't want to append dummy data and additional series elements. Is overlay the only option to add axis?
<Chart
width={"800px"}
height={"600px"}
chartType="BubbleChart"
loader={<div>Loading Chart</div>}
data={[
["Age", "Weight", "4", "Group", "Size"],
["Name", -12, 34, 1, 7],
["Name", -5.5, 6, 1, 8],
["Name", 22, 35, 1, 4],
["Name", 14, 4, 2, 5],
["Name", -5, -5, 2, 6],
["Name", 15, 34, 3, 1],
["Name4", 7, -21, 4, 22]
]}
options={{
bubble: {},
chartArea: { left: 20, top: 0, width: "90%", height: "95%" },
colors: ["red", "green", "yellow", "blue"],
hAxis: {
title: "Low Importance",
ticks: "none",
baseline: 0,
ticks: ["none"],
minValue: -50,
gridlines: { count: 0 },
maxValue: 50
},
vAxis: {
title: "Low Urgency",
baseline: 0,
ticks: ["none"],
minValue: -50,
gridlines: { count: 0 },
maxValue: 50
},
legend: "none"
}}
/>;
As you can see in the graph below, I can only get left and bottom labels but not top and right. Any ideas how I could achieve that using google-chart library for reactjs?