Why won't "angleString" update in Animated rotate? - javascript

I'm trying to create a component on my screen, where the user is able to use their finger to rotate the componnent around its center. I have been able to get the angle and consistently update it (verified through console.log), but the value is not being passed rotate under Animated.view.
Here is my code:
class Spinner extends React.Component {
constructor(props){
super(props);
this.angleCalculation = this.angleCalculation.bind(this);
this.state = {
angleString: '0deg'
}
}
angleCalculation(x,y){
xValue = x - 50
yValue = (y * -1) + 150
angle = Math.atan(yValue/xValue) * (180/Math.PI)
this.state.angleString = angle + 'deg'
console.log(this.state.angleString)
}
render() {
return(
<View style={styles.container}>
<Animated.View
style={styles.box, {
backgroundColor: 'blue',
width:100,
height:300,
transform:[{ rotate: this.state.angleString
}]
}}
onStartShouldSetResponder={(evt) => true}
onResponderGrant={(evt) => {this.angleCalculation(evt.nativeEvent.locationX,evt.nativeEvent.locationY)}}
onResponderMove={(evt) => {this.angleCalculation(evt.nativeEvent.locationX,evt.nativeEvent.locationY)}}
onResponderRelease={(evt) => {this.angleCalculation(evt.nativeEvent.locationX,evt.nativeEvent.locationY)}}
/>
</View>
)
}
}
The console.log consistently updates as I moved my finger in a circular fashion. And it does so in the angleString form that I want. In my this.state, I set an initial value of '0 deg' but I am not sure why it is updating under 'rotate'.
What can I do to make it update in real-time? So the rotate accepts the updated values and follows along with the user's finger?
Thanks for any help that you can provide!

Related

Custom react-native circle menu help needed

I need to build a custom circle menu in react-native. I am currently building this menu with expo. It needs to have a circle in the middle of the screen with a logo/text and when you press and hold on the circle, smaller circles buttons come out from the middle and grow into place around the main circle, evenly spaced. Then, while still pressing, as you move your finger towards these smaller circles, they get bigger to reveal an icon and or text. When you release your finger on one of these outer circle buttons, it then takes you to that part of the app.
The closest module I found for my need is this one:
https://github.com/Ramotion/react-native-circle-menu
It did not work out of the box. I had to modify it to get it working with the most recent react-native/expo and the progress portion also doesn't work, so I commented that out as well.
As far as my need and what I have, I was able to get the circle in the middle with text, you press and hold and the smaller circle buttons come out and are evenly spaces around it, but when you drag to them and let go, im not sure which events on which components would register how I need.
Here is the code that renders the main menu button:
return <Animated.View style={{
transform: [
{
scaleX: this.state.animation.interpolate({
inputRange: [0, .2, .75, 1],
outputRange: [1.5, 1.2, .5, 1]
})
},
{
scaleY: this.state.animation.interpolate({
inputRange: [0, .2, .75, 1],
outputRange: [1.5, 1.2, .5, 1]
})
}
]
}}>
<TouchableOpacity
onPressIn={this.openMenu}
onPressOut={this.closeMenu}
style={styles.closedButton}
activeOpacity="1">
<Text style={styles.closedButtonText}>XYZ</Text>
</TouchableOpacity>
</Animated.View>
This renders the smaller circle buttons that are around the outside of the main menu button above:
renderActions() {
if (!this.state.active) {
return null
}
// Defines the amount of angle to increase for each item so they do not overlap
const increase = Math.PI * 2 / this.props.items.length;
// Define initial angle
let angle = -Math.PI / 2;
// Loop through each action (item) to add them to the display
return this.props.items.map((item, index) => {
// Angle first starts at the initial angle
const btnAngle = angle;
// Then increases the angle by 'increase' for each button
angle += increase;
// Displays the individual action (item)
return <ActionIcon
key={index}
animation={this.state.animation}
angle={btnAngle}
bgColor={this.props.bgColor}
buttonColor={item.color}
icon={item.name}
radius={this.props.radius - this.props.itemSize}
onPress={() => {
this.closeMenu();
this.props.onPress(index);
}}
size={this.props.itemSize}
style={[styles.actionContainer, { position: 'absolute' }]}
/>
})
}
and finally, here is how the circle menu is being called from the main App.js
items = [
{
name: 'md-home',
color: '#298CFF',
},
{
name: 'md-search',
color: '#30A400',
},
{
name: 'md-time',
color: '#FF4B32',
},
{
name: 'md-settings',
color: '#8A39FF',
},
{
name: 'md-navigate',
color: '#FF6A00',
},
];
onPress = index => console.log(`${this.items[index].name} icon pressed!`);
render() {
return <View style={styles.container}>
<CircleMenu
bgColor="#0E1329"
items={this.items}
onPress={this.onPress}
/>
</View>
}
If there is any other information I can provide, please let me know. Thanks in advance.

How can I select VerticalBarSeries from a ReactVis series plot?

ReactVis API here outlines what is available which might be helpful. I have successfully added a "drag to select" function to my custom code. In the picture, we would be selecting 13 to 17 and turn the fill value to red for example.
More pseudo code question but code would be awesome.
A container div in a Hook would look like this:
<div className="unselectable"
style={{ position: 'relative' }}
onMouseDown={(e) => {
if (!x && !y) {
setX(e.clientX); setY(e.clientY)
}
}}
onMouseMove={(e) => {
if (x && y) {
const newX = e.clientX; const newY = e.clientY;
setX1(newX); setY1(newY);
setRect(
<div style={{
zIndex: 10000,
position: 'fixed',
left: x > newX ? newX : x, top: y > newY ? newY : y,
width: Math.abs(newX - x), height: Math.abs(newY - y),
backgroundColor: 'gray', opacity: 0.2
}} />
)
}
}}
onMouseUp={(e) => {
setX(null); setY(null); setX1(null); setY1(null);
setRect(null);
}}
>
<XYPlot>
//...
</XYPlot>
{rect}
</div>
Thank you very much.
In order to achieve "selected" vs "notSelected" state of the series, we need to use "datawithcolor" objects as our data object: {x: foo, y: bar, color:0}. Passing such an array to a react-series would be sufficient to render different coloured series (bars for example). For example: [{x: foo, y: bar, color:0}] where colorDomain would be [0, 1] and the colorRange would be some blue and red.
In order to have a listed of selected "values", we can hold on to a list of values in a react state (hooks or class), either during a dragging of a rectangle or after the rectangle has been drawn, in my case only during dragging. Every time the rectangle changes, the list must be updated.
On re-render generate a new "datawithcolor" array for React-vis bar-series with colour ranges and domains defined using indexes returned from onNearestX or similar React-vis callback.
How do we update the selected list?
<ReactSeries
// see React-vis source of abstract-series.js for details
/*
onNearestX(value, {
innerX: xScaleFn(value),
index: valueIndex,
event: event.nativeEvent
});
*/
onNearestX={(datapoint, { index, innerX }) => {
// rect is mentioned in the question
if (rect && isWithinRect({ x, x1, value: innerX })) {
if (!selected.includes(index)) { // prevent rerender
setSelected([...selected, index]);
}
} else {
if (!rect) {
setSelected([index]); // single hover
}
}
}}
colorDomain={[0, 1]}
colorRange={['red', 'blue']}
data={dataWithColor} />
For full example see this.
Things to remember, managing the rectangle and selected list are two different things. React-vis colorRange should be clear by now, the docs were not so helpful to me.

Incorrect geometric shape

I'm trying to create geometric shapes (circles) that change color depending on certain values ​​assumed by a variable.
In theory I set the values ​​of these geometric shapes so that they turn out to be circles, instead they turn out to be squares. Can you tell me why?
this.state = {
pressColorDx1: "",
//...
}
//....
if (this.pressDx_p1 >= 10) { this.setState({ pressColorDx1: "#00438c" }) }
else { this.setState({ pressColorDx1: '#afeeee' }) }
render() {
return (
//..
<View style={{width: 15,height: 15,borderRadius: 15/2,backgroundColor: this.state.pressColorDx1,right: 30, top: 40}}></View>
)}
this should be a circle, but it is displayed like a square.
It's not a problem of top and right.. I have tried also borderRadius: 50% and it doesn't work.
You can see there https://snack.expo.io/#keyz23/92058c

How to implement a design with react-spring?

In the project's repo the react-redux app is set with CSS in JS files. Now I'm supposed to animate pictures with mouse hover just like this website : https://www.madwell.com/
The component was initially a functional component and I have changed that to a class based component as follows:
```
class BannerContainer extends React.Component{
constructor(props){
super(props);
this.state = {
x: 0,
y: 0
};
this.handleMouseMove = this.handleMouseMove.bind(this);
}
componentDidMount(){
window.addEventListener("mousemove", this.handleMouseMove);
}
componentWillUnmount(){
window.removeEventListener('mousemove', this.handleMouseMove);
}
handleMouseMove({ x, y }){
this.setState({
x : x / window.innerWidth,
y : y / window.innerHeight
})
}
render(){
const { banner = {} } = this.props;
const {
title = ' <br> ',
text = ' <br> ',
image__google_350x80 = '',
image__app_350x80 = '',
image__bg1_1166x1878 = '',
image__bg2_961x1782 = '',
image__curve_730x151 = ''
} = banner;
return (
<Container>
<BGPatch>
{console.log(this.state.x , this.state.y)}
<img src={'/images/bg_purple.jpg'} alt="" />
</BGPatch>
```
In this example I am able to get listen to the mouse-move event and and get the x & y coordinates accordingly. But now I have to use react-spring library to implement it so how do I do that? Also the CSS should be written in separate JS file for each component where as in react-spring example they directly modify the opacity or trasform in the Spring component directly as well which is what I don't want
the spring component example given on their docs
<Spring from={{ opacity: 0 }} to={{ opacity: 1 }}>
{props => <div style={props}>hello</div>}
</Spring>
Parallaxing effects are relatively easy to do if you have mouse coordinates, here's an example with a similar effect: https://twitter.com/0xca0a/status/1058505720574959616
It uses hooks, but the same thing applies to a regular spring. Though this is probably a good example even, because it doesn't re-render the component on mousemove, which is very fast. You wouldn't use rotates in your case, translates i guess, but the point is, you receive x/y and use it to interpolate your images into position.
EDIT:
I've forked the example, this version uses translates: https://codesandbox.io/embed/r5x34869vq

React-Three-Renderer refs not current in componentDidUpdate (MVCE included)

I'm using react-three-renderer (npm, github) for building a scene with three.js.
I'm having a problem that I've boiled down to an MVCE. Refs aren't updating in the sequence I expect them to. First, here's the main code to look at:
var React = require('react');
var React3 = require('react-three-renderer');
var THREE = require('three');
var ReactDOM = require('react-dom');
class Simple extends React.Component {
constructor(props, context) {
super(props, context);
// construct the position vector here, because if we use 'new' within render,
// React will think that things have changed when they have not.
this.cameraPosition = new THREE.Vector3(0, 0, 5);
this.state = {
shape: 'box'
};
this.toggleShape = this.toggleShape.bind(this);
}
toggleShape() {
if(this.state.shape === 'box') {
this.setState({ shape: 'circle' });
} else {
this.setState({ shape: 'box' });
}
}
renderShape() {
if(this.state.shape === 'box') {
return <mesh>
<boxGeometry
width={1}
height={1}
depth={1}
name='box'
ref={
(shape) => {
this.shape = shape;
console.log('box ref ' + shape);
}
}
/>
<meshBasicMaterial
color={0x00ff00}
/>
</mesh>;
} else {
return <mesh>
<circleGeometry
radius={2}
segments={50}
name='circle'
ref={
(shape) => {
this.shape = shape;
console.log('circle ref ' + shape);
}
}
/>
<meshBasicMaterial
color={0x0000ff}
/>
</mesh>
}
}
componentDidUpdate() {
console.log('componentDidUpdate: the active shape is ' + this.shape.name);
}
render() {
const width = window.innerWidth; // canvas width
const height = window.innerHeight; // canvas height
var position = new THREE.Vector3(0, 0, 10);
var scale = new THREE.Vector3(100,50,1);
var shape = this.renderShape();
return (<div>
<button onClick={this.toggleShape}>Toggle Shape</button>
<React3
mainCamera="camera"
width={width}
height={height}
onAnimate={this._onAnimate}>
<scene>
<perspectiveCamera
name="camera"
fov={75}
aspect={width / height}
near={0.1}
far={1000}
position={this.cameraPosition}/>
{shape}
</scene>
</React3>
</div>);
}
}
ReactDOM.render(<Simple/>, document.querySelector('.root-anchor'));
This renders a basic scene with a green box, a fork of the example on react-three-renderer's github landing page. The button on the top left toggles the shape in the scene to be a blue circle, and if clicked again, back to the green box. I'm doing some logging in the ref callbacks and in componentDidUpdate. Here's where the core of the problem I'm encountering occurs. After clicking the toggle button for the first time, I expect the ref for the shape to be pointing to the circle. But as you can see from the logging, in componentDidUpdate the ref is still pointing to the box:
componentDidUpdate: the active shape is box
Logging in lines after that reveals the ref callbacks are hit
box ref null [React calls null on the old ref to prevent memory leaks]
circle ref [object Object]
You can drop breakpoints in to verify and to inspect. I would expect these two things to happen before we enter componentDidUpdate, but as you can see, it's happening in reverse. Why is this? Is there an underlying issue in react-three-renderer (if so, can you diagnose it?), or am I misunderstanding React refs?
The MVCE is available in this github repository. Download it, run npm install, and open _dev/public/home.html.
Thanks in advance.
I checked the source in react-three-renderer. In lib/React3.jsx, there is a two phased render.
componentDidMount() {
this.react3Renderer = new React3Renderer();
this._render();
}
componentDidUpdate() {
this._render();
}
The _render method is the one that seems to loads the children - the mesh objects within Three.
_render() {
const canvas = this._canvas;
const propsToClone = { ...this.props };
delete propsToClone.canvasStyle;
this.react3Renderer.render(
<react3
{...propsToClone}
onRecreateCanvas={this._onRecreateCanvas}
>
{this.props.children}
</react3>, canvas);
}
The render method draws the canvas and does not populate the children or invoke Three.
render() {
const {
canvasKey,
} = this.state;
return (<canvas
ref={this._canvasRef}
key={canvasKey}
width={this.props.width}
height={this.props.height}
style={{
...this.props.canvasStyle,
width: this.props.width,
height: this.props.height,
}}
/>);
}
Summarizing, this is the sequence:
App Component render is called.
This renders react-three-renderer which draws out a canvas.
componentDidUpdate of App component is called.
componentDidUpdate of react-three-renderer is called.
This calls the _render method.
The _render method updates the canvas by passing props.children (mesh objects) to the Three library.
When the mesh objects are mounted, the respective refs are invoked.
This explains what you are observing in the console statements.

Categories

Resources