I tried to create a lucky draw wheel using reactjs, first, I need to place all the input data to a certain XY position. Below is the expected output XY position example what I need.
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
React.createElement('div', { className: '_data'},
renderData.map((index,item)=>{
var itemPosition = index / renderData.length * 360;
var itemX = itemPosition * Math.PI/180;
var itemY = itemPosition * Math.PI/180;
return React.createElement('div', { className: '_items',
style:{top:itemX,left:itemY}
},item);
})
)
So I use createElement to create div for each of the data, then using top and left for XY position.
How to calculate the XY position for each div
Update
After tried the #keikai answer
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 350;
const len = renderData.length;
const radiusList = renderData.map(x => (360 / len) * (x - 1));
const positionPairList = radiusList.map(x => ({
x: Math.sin((Math.PI * x) / 180) * r,
y: Math.cos((Math.PI * x) / 180) * r
}));
React.createElement('div', { className: '_data'},
renderData.map((item, index) => {
return React.createElement('div', { className: `_items`,
style:{top:`${positionPairList[index].x.toFixed(2)}px`,left:`${positionPairList[index].y.toFixed(2)}px`}
},item);
})
)
all data are rotated 0deg
child div still not place to the right position inside parent div
for clockwise, it starts from 10?
Update: rotate display with clock styles
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const n = 12;
const r = 500;
const radiusList = Array.from(Array(n).keys()).map(x => (360 / n) * x);
const positionPairList = radiusList.map(item => ({
x: Math.sin((Math.PI * item) / 180) * r,
y: Math.cos((Math.PI * item) / 180) * r
}));
return (
<div className="App">
{positionPairList.map((item, index) => {
const offset = index === 0 ? n : 0;
return (
<div
className="Parts"
style={{
top: `${r - item.y.toFixed(0)}px`,
right: `${r + 200 - item.x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`
}}
>
{index + offset}
</div>
);
})}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
I'm trying to center the viewport on an object and zoom in/out when i click a button. I want to have an animation. To do this I'm using setInterval and moving the viewport in increments like so:
const objectCenterCoordenates = {
x: Math.round(rect1.left + rect1.width / 2),
y: Math.round(rect1.top + rect1.height / 2)
};
const centeredCanvasCoordenates = {
x: objectCenterCoordenates.x - canvas.width / 2,
y: objectCenterCoordenates.y - canvas.height / 2
};
let currentPoint = {
x: Math.round(canvas.viewportTransform[4]) * -1,
y: Math.round(canvas.viewportTransform[5]) * -1
};
console.log("Start animation");
let animation = setInterval(() => {
console.log("New frame");
if (canvas.getZoom() !== 2) {
let roundedZoom = Math.round(canvas.getZoom() * 100) / 100;
let zoomStep = roundedZoom > 2 ? -0.01 : 0.01;
let newZoom = roundedZoom + zoomStep;
canvas.zoomToPoint(
new fabric.Point(currentPoint.x, currentPoint.y),
newZoom
);
}
let step = 5;
let vpCenter = {
x: Math.round(canvas.getVpCenter().x),
y: Math.round(canvas.getVpCenter().y)
};
if (
vpCenter.x === objectCenterCoordenates.x &&
vpCenter.y === objectCenterCoordenates.y
) {
console.log("Animation Finish");
clearInterval(animation);
}
let xDif = Math.abs(vpCenter.x - objectCenterCoordenates.x);
let yDif = Math.abs(vpCenter.y - objectCenterCoordenates.y);
let stepX =
Math.round(vpCenter.x) > Math.round(objectCenterCoordenates.x)
? -step
: step;
if (Math.abs(xDif) < step)
stepX =
Math.round(vpCenter.x) > Math.round(objectCenterCoordenates.x)
? -xDif
: xDif;
let stepY =
Math.round(vpCenter.y) > Math.round(objectCenterCoordenates.y)
? -step
: step;
if (Math.abs(yDif) < step)
stepY =
Math.round(vpCenter.y) > Math.round(objectCenterCoordenates.y)
? -yDif
: yDif;
currentPoint = {
x: currentPoint.x + stepX,
y: currentPoint.y + stepY
};
canvas.absolutePan(new fabric.Point(currentPoint.x, currentPoint.y));
}, 4);
But sometimes, I haven't been able to determine why, it gets stuck in a loop moving a few pixels one way then going back the other. And the zoom is well implemented, since it's possible to get to the object before it finished zooming in/out.
Here is a codepen with my code: https://codepen.io/nilsilva/pen/vYpPrgq
I would appreciate any advice on making my algorithm better.
Looks like you have an exact match requirement for your clearInterval ...
if (
vpCenter.x === objectCenterCoordenates.x &&
vpCenter.y === objectCenterCoordenates.y
) {
console.log("Animation Finish");
clearInterval(animation);
}
The case that you describe stuck in a loop moving one way then going back the other is because the exact match it's never done, could be because the step you are taking or it could be a rounding issue
You can use Math.hypot to check if the two points are close enough, read more here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
The condition will look like:
if (Math.hypot(
vpCenter.x - objectCenterCoordenates.x,
vpCenter.y - objectCenterCoordenates.y
) < step) {
console.log("Animation Finish");
clearInterval(animation);
}
I've created a react-three-fiber functional component to load a glb file of a butterfly with animation and I am returning a primitive with the scene of the glb passed as an object prop. It is nested in a mesh, that is nested in a scene, that is nested in a group.
function Butterfly({ speed, factor, url, ...props }) {
const { scene, nodes, materials, animations } = useLoader(GLTFLoader, url);
const group = useRef()
const [mixer] = useState(() => new THREE.AnimationMixer())
useEffect(() => mixer.clipAction(animations[0], group.current).play(), [])
useFrame((state, delta) => {
group.current.rotation.y -= Math.sin((delta * factor) / 2) * Math.cos((delta * factor) / 2) * 2
mixer.update(delta * speed)
})
return (
<group ref={group} dispose={null}>
<scene name="Scene" {...props}>
<mesh
name="Object_0"
>
<primitive object={scene}/>
</mesh>
</scene>
</group>
)
}
This component is then returned in separate function in each iteration of an array.
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
return copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})
}
However, this function is only returning one butterfly instead of 100.
single butterfly
I think my issue has something to do with the return in the Butterfly component. I've tried only returning the primitive with props and a ref, but that doesn't render anything. I've tried going through the console.log of the the glb file and found the geometry and tried passing that as a prop to the mesh along with the materials, but that only rendered a white butterfly shape without the animation from the scene. Why is this only returning 1 butterfly instead of 100?
I assume Butterflies() is in Butterflies.js and you are trying return many <Butterfly> here, however react component can only have one parent.
try this in Butterflies():
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
return(
<>
{copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})}
</>)}
Or store it in a variable first, for the example:
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
const items=copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})
return(
<>
{items}
</>
)}
Hi as stated above my output keeps showing up as NaN and I'm really struggling to fix it.
There are ultimately three classes that will touch the code. App.js, Calculate.js, and Calculations.js where all of the functions are stored. In Calculate.js, I am importing two functions from Calculations.js with the hopes of producing their output after data is taken in from four input fields and a button press. The code for all three files is below.Please help, I truly am lost now.
Edit: it should be noted the calculations were given to us and are basically unmodifiable.
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Calculate from './components/Calculate';
import Clear from './components/Clear';
//need to put clear underneath Calculate
export default function App() {
return (
<View style={styles.container}>
<Text> 'Please enter your coordinates you wish to calculate.' </Text>
<Calculate buttonTitle = 'Calculate'/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Calculate.js
import React, { useState} from 'react';
import {StyleSheet, Text, View, TextInput} from 'react-native';
import {Button} from 'react-native-elements';
import {computeDistance, computeBearing} from './Calculations';
const Calculate = ({buttonTitle, lat1, lon1, lat2,lon2, distance, bearing}) => {
const [state, setState] = useState({lat1: '', lon1: '', lat2: '', lon2: '', distance: '', bearing: ''});
const updateStateObject = (vals) =>{
setState({
...state,
...vals,
});
};
return (
<View style={styles.container}>
<TextInput
placeholder = 'Starting latitude'
onChangeText = {(lat1) => updateStateObject({lat1: lat1})} //or you could do (val) => {setName(val);}
value = {state.lat1}/>
<TextInput
placeholder = 'Starting longitude'
onChangeText = {(lon1) => updateStateObject({lon1: lon1})} //or you could do (val) => {setName(val);}
value = {state.lon1}/>
<TextInput
placeholder = 'Ending latitude'
onChangeText = {(lat2) => updateStateObject({lat2: lat2})} //or you could do (val) => {setName(val);}
value = {state.lat2}/>
<TextInput
placeholder = 'Ending longitude'
onChangeText = {(lon2) => updateStateObject({lon2: lon2})} //or you could do (val) => {setName(val);}
value = {state.lon2}/>
<Button
title= {buttonTitle}
onPress = {() =>{
state.distance = computeDistance(lat1, lon1, lat2, lon2);
state.bearing = computeBearing(lat1, lon1, lat2, lon2);
updateStateObject({distance: `Distance: ${distance}`});
updateStateObject({bearing: `Bearing: ${bearing}`});
}} />
<Text> {state.distance}</Text>
<Text> {state.bearing}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default Calculate;
Calculations.js
// Converts from degrees to radians.
function toRadians(degrees) {
return (degrees * Math.PI) / 180;
}
// Converts from radians to degrees.
function toDegrees(radians) {
return (radians * 180) / Math.PI;
}
// Computes distance between two geo coordinates in kilometers.
export function computeDistance(lat1, lon1, lat2, lon2) {
console.log(`p1={${lat1},${lon1}} p2={${lat2},${lon2}}`);
var R = 6371; // km (change this constant to get miles)
var dLat = ((lat2 - lat1) * Math.PI) / 180;
var dLon = ((lon2 - lon1) * Math.PI) / 180;
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return `${round(d, 3)} km`;
}
// Computes bearing between two geo coordinates in degrees.
export function computeBearing(startLat, startLng, destLat, destLng) {
startLat = toRadians(startLat);
startLng = toRadians(startLng);
destLat = toRadians(destLat);
destLng = toRadians(destLng);
var y = Math.sin(destLng - startLng) * Math.cos(destLat);
var x =
Math.cos(startLat) * Math.sin(destLat) -
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
var brng = Math.atan2(y, x);
brng = toDegrees(brng);
return (brng + 360) % 360;
}
function round(value, decimals) {
return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
}
Output
So apparently I have had a misunderstanding on what state is and how it is used. The working button should read as the following:
<Button
title={buttonTitle}
onPress = {() =>{updateStateObject({
distance: `Distance: ${computeDistance( parseFloat(lat1), parseFloat(lon1), parseFloat(lat2), parseFloat(lon2))}`,
bearing: `Bearing: ${computeBearing( parseFloat(lat1), parseFloat(lon1), parseFloat(lat2), parseFloat(lon2))}`,
})
}}
/>
From the last question I asked Calculate Wheel Data XY Position , I have get the X,Y and Rotation for the div child.
Let say, the parent div width is 414px , so I divide by 2 is 207 for const r
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 207;
const len = renderData.length;
const radiusList = Array.from(Array(len).keys()).map(x => (360 / len) * x);
const positionPairList = radiusList.map(x => ({
x: Math.sin((Math.PI * x) / 180) * r,
y: Math.cos((Math.PI * x) / 180) * r
}));
React.createElement('div', { className: '_data'},
renderData.map((item, index) => {
return React.createElement('div', { className: `_items`,
style:{
top:`${r - positionPairList[index].y.toFixed(0)}px`,
right:`${r - positionPairList[index].x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`},
},item);
})
)
Below is the output result
The child X and Y still no position in the right place, note that I cannot manually add the top or right position value, the value must use calculation.
I'm trying to get a bus with its compass move along the line. However, I can't get the compass rotating properly, it rotates and also move in a very strange way.
Could you please help me find what has gone wrong?
Here is the link to Fiddle: https://jsfiddle.net/ugvapdrj (I've not fixed the CSS yet so it overflows to the right.)
Thanks!
Code:
<div id="drawing"></div>
let canvas = SVG('drawing').size(2000, 2000)
let compassSize = 42;
let lineColour = '#f00';
let Rsc2muVkt = canvas
.path(
'M736 96V72c0-13-11-24-24-24h-75c-18 0-35 7-48 18l-104 94c-23 21-54 32-85 32H0'
)
.stroke({
color: lineColour
})
.fill('none');
// Buses
let bus = canvas.image(
'https://s3-ap-southeast-1.amazonaws.com/viabus-develop-media-resource-ap-southeast/Vehicle+Images/entity/mu/veh_core_mu_06.png',
compassSize
);
let busCompass = canvas.image(
'https://s3-ap-southeast-1.amazonaws.com/viabus-develop-media-resource-ap-southeast/Network+Images/Azimuth/Public/veh_ring_white.png',
compassSize
);
moveBus(bus, busCompass, Rsc2muVkt, 0);
for (let i = 0; i <= 100; i++) {
setTimeout(() => {
moveBus(bus, busCompass, Rsc2muVkt, i);
}, 1000 + i * 200);
}
function moveBus(bus, busCompass, path, to) {
const p = path.pointAt(to / 100 * path.length());
const newPosition = {
x: p.x,
y: p.y
};
const oldX = bus.cx();
const oldY = bus.cy();
bus.center(newPosition.x, newPosition.y);
busCompass.center(newPosition.x, newPosition.y);
const newX = bus.cx();
const newY = bus.cy();
const dx = newX - oldX;
const dy = newY - oldY;
const rotatingAngle = Math.atan2(dy, dx) * 180 / Math.PI;
busCompass.rotate(rotatingAngle + 90);
}
The compass is still rotated when you try to center it on the next frame of animation. This causes the busCompass.center(...) call to use the rotated coordinate system.
You should first call busCompass.rotate(0); to reset the rotation, then busCompass.center(...) will work as expected and the final rotation will complete.