not getting new values from useState react - javascript

Learning react by coding, here i'm using 'react-zoom-pan-pinch' for mouse scroll wheel, so when i zoomIn using mouse scroll and its scale gets bigger than '1.311' then it should make circle smaller ("10"), here i have created demo of what i want to achieve.
https://codesandbox.io/s/react-zoom-pan-pinch-forked-svx5v6?file=/src/index.js
you can check from console when value gets bigger that circle becomes smaller,
but in my real code i'm using 'viewGenerator' (react-d3-graph) and customNode where i have that svg and circle
import { Graph } from "react-d3-graph";
const [circleR, setCircleR] = useState("50");
const zoomChange = (event: any) => {
if (event.state.scale < 1.311) {
setCircleR("50");
}
if (event.state.scale > 1.311) {
setCircleR("10");
}
console.log("event state scale ", event.state.scale);
console.log("event circleR ", circleR);
};
function CustomNode(props) {
return (
<div>
<svg viewBox="-1 -1 2 2">
<circle
cx="0"
cy="0"
r={circleR}
strokeWidth="0.1"
stroke= "black"
fill= "orange"
/>
</svg>
</div>
);
}
const default_config = {
d3_config: {
nodeHighlightBehavior: false,
staticGraphWithDragAndDrop: true,
automaticRearrangeAfterDropNode: false,
d3: { disableLinkForce: true },
height: 512,
// width: 576,
minZoom: 1,
maxZoom: 1,
node: {
color: "lightblue",
size: 500,
highlightStrokeColor: "blue",
fontSize: 20,
highlightFontSize: 22,
labelProperty: (node: default_config) =>
node.name ? node.name : node.id,
viewGenerator: (node) => (
<CustomNode node={node} focusId={null} />
),
}
};
const [config, setConfig] = useState(default_config);
<Graph
config={config.d3_config}
/>
Problem is 'r={circleR}' is just taking default value from state, and its not updating it when i'm zooming in or zooming out.
Inside my 'zoomChange ' function i have two console.logs and i can check that it gives me right values and changes between '10' and '50' (zoomChange function works fine), then what is the reason i'm not getting updated value to 'r={circleR}' ?
writing values to it like 'r="50" or r="10"' will work but i want updated values according to setState.
english is not my mother language so could be mistakes !

Related

Is there a way to modify an individual column in a HexagonLayer?

So I created a flat HexagonLayer and have various interactions tied to the onClick prop as shown below.
My goal now is to modify the individual column that was clicked by adding an outline, changing the color for that column, or something else along those lines. I could probably come up with a brute force resolution by adding another layer beneath this one, but the documentation doesn't go into much detail on this and I wanted to see if there was possibly another route to take that would modify the existing layer.
I would also like to obtain info such as the number of points within a given column, but I would imagine that's related to the first part.
Here's what I currently have:
// Here's where I'm creating the settings for the HexagonLayer
const hexagonLayer = {
id: 'hexagon-layer',
data: props.G.cells,
pickable: true,
extruded: true,
radius: 50,
elevationScale: 0,
autoHighlight: true,
opacity: 0.3,
getPosition: d => d.COORDINATES,
// Here's the onClick interactions
onClick: (layer, $event) => {
// I can obtain some information from the layer variable here,
// but not nearly enough for what I'm trying to accomplish
switch (props.ctx.phase) {
case 'setup':
props.moves.placeUnit(layer.object.position)
break
case 'play':
// Replace with active unit functionality
const activeUnit = Object.keys(props.G.players[props.ctx.currentPlayer].pieces)[0]
props.moves.moveUnit(activeUnit, layer.index, layer.object.position)
break
default:
console.error('Unknown phase', props.ctx.phase)
}
return true
}
};
// Check some incoming data to add the ducks shown in the screenshot
// Not necessarily related to the question, but they were in the screenshot so I included it
useEffect(() => {
const tempPieces = []
props.G.players.forEach(player => {
Object.values(player.pieces).forEach(piece => {
tempPieces.push(
createScenegraphLayer(
piece.id,
UNIT_MODEL_MAP[piece.type],
piece.coordinates, 30
)
)
})
})
setPieces(tempPieces)
}, [props.G.cells, props.G.players])
// Rendering the layers
return (
<div className="Map">
<DeckGL
initialViewState={INITIAL_VIEW_STATE}
controller={true}
debug={true}>
<ReactMapGL mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN}>
<Layer {...threeDLayer} />
</ReactMapGL>
<HexagonLayer {...hexagonLayer} />
{pieces.map(piece => (
<ScenegraphLayer key={piece.id} {...piece} coordinates={piece.data[0].coordinates} />
))}
</DeckGL>
<ToastContainer />
</div>
);
My goal now is to modify the individual column that was clicked by adding an outline, changing the color for that column, or something else along those lines.
To change the color of the clicked column you could utilize highlightColor and highlightedObjectIndex in the following way:
const App = () => {
const [highlightedObjectIndex, setHighlightedObjectIndex] = useState(-1);
const onClick = info => {
if (info.object) {
setHighlightedObjectIndex(info.object.index);
}
};
const layers = [
new HexagonLayer({
id: "hexagon-layer",
data,
pickable: true,
radius: 200,
getPosition: d => d.COORDINATES,
highlightColor: [0, 0, 255],
highlightedObjectIndex,
updateTriggers: {
highlightedObjectIndex
},
onClick
})
];
return (
<DeckGL
initialViewState={INITIAL_VIEW_STATE}
controller={true}
layers={layers}
>
</DeckGL>
);
};

Customizing styles on Slider Material-UI (React)

I'm trying to make a slider with restricted values with Material UI and React. I have copy the basic implementation from the documentation page and it seem's not to need CSS to function. But when I use it in my project, the labels appear overlapped as the color of the line representing the chosen value (with 0 value, the line is light blue, with 5 value, all the line is heavy blue).
This is the code I have implemented:
const useStyles = makeStyles({
root: {
width: 300,
},
});
const marks = [
{
value: 1,
label: 'Nada',
},
{
value: 2,
label: 'Un poco',
},
{
value: 3,
label: 'Moderado',
},
{
value: 4,
label: 'Bastante',
},
{
value: 5,
label: 'Totalmente'
}
];
function valuetext(value) {
return `${value}`;
}
function valueLabelFormat(value) {
return marks.findIndex((mark) => mark.value === value) + 1;
}
export default function DiscreteSlider() {
const classes = useStyles();
return (
<div className={classes.root}>
<Typography id="discrete-slider-restrict" gutterBottom>
Restricted values
</Typography>
<Slider
defaultValue={0}
valueLabelFormat={valueLabelFormat}
getAriaValueText={valuetext}
aria-labelledby="discrete-slider-restrict"
step={null}
valueLabelDisplay="auto"
marks={marks}
/>
</div>
);
}
And the Slider looks like that:
How can I change the overlapping and make it functional? Is there any Slider prop that I can use?
Thank you very much!
Set max for the Slider to 5 - currently it is defaulting to 100 (see docs for default value). Currently, your mark values are 1 to 5 so they are being squeezed in that tiny area
<Slider
max={5} // <-- set max
defaultValue={0}
valueLabelFormat={valueLabelFormat}
getAriaValueText={valuetext}
aria-labelledby="discrete-slider-restrict"
step={null}
valueLabelDisplay="auto"
marks={marks}
/>
and allocate more width to accomodate the labels
const useStyles = makeStyles({
root: {
width: 600
}
});

What is the correct way to set the new coordinates of a view after translation animation in react-native?

I am trying to run a few simple animations using react-native-animatable library. (But I believe the question should be generic to any react animations so adding other tags as well.)
The problem is, in the first time, the image animates just as expected. But when aimed to start second animation animation with the gesture, the image translation starts from its original coordinates.
A search yielt, in Android development (which is obviously not my case) there seems a method, setFillAfter which sets the coordinate after the animation.
My question is, how to set the location (left / top values for example) to the final translated point so that consecutive animation starts from the point the previous translation left.
The expo snack for below code block is here.
import * as React from 'react';
import { Image, StyleSheet, ImageBackground } from 'react-native';
import * as Animatable from 'react-native-animatable';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
import testImg from './test.png';
import backImg from './back.png';
export default class App extends React.Component {
onTestMove(event) {
this.testAnimRef.transitionTo({
translateX: event.nativeEvent.translationX,
translateY: event.nativeEvent.translationY,
}, 0);
}
render() {
return (
<ImageBackground source={backImg} style={{ flex: 1 }} >
<PanGestureHandler
key={`test`}
onGestureEvent={(e) => { this.onTestMove(e) }}
onHandlerStateChange={e => { }}
>
<Animatable.View style={styles._animatable_view}
ref={((ref) => { this.testAnimRef = ref }).bind(this)}
useNativeDriver={true}
>
<Image source={testImg} style={styles._image} />
</Animatable.View>
</PanGestureHandler>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
_image: {
width: 50,
height: 25,
resizeMode: 'contain',
backgroundColor: 'black',
borderColor: 'gainsboro',
borderWidth: 2,
},
_animatable_view: {
position: "absolute",
top: 200,
left: 100,
},
});
I had the same problem trying to move around some cards in a view, and upon further dragging, they would reset to their origin.
My theory is/was that while the translated view would have its x / y coordinates translated, this would not apply to the parent of that view, and so the animated event passed from that component would initially have the original coordinates (nuke me if I'm wrong here)
So my solution was to keep an initial offset value in state, and maintain this every time the user releases the dragged motion
_onHandleGesture: any
constructor(props: OwnProps) {
super(props)
this.state = {
animationValue: new Animated.ValueXY({ x: 0, y: 0 }),
initialOffset: { x: 0, y: 0 },
}
this._onHandleGesture = (e: PanGestureHandlerGestureEvent) => {
this.state.animationValue.setValue({
x: e.nativeEvent.translationX + this.state.initialOffset.x, <- add initial offset to coordinates passed
y: e.nativeEvent.translationY + this.state.initialOffset.y,
})
}
}
_acceptCard = (cardValue: number) => {
const { targetLocation, onAccept } = this.props
const { x, y } = targetLocation
onAccept(cardValue)
Animated.spring(this.state.animationValue, {
// Some animation here
}).start(() => {
this.setState({ initialOffset: targetLocation }) // <- callback to set state value for next animation start
})
}
and the render method
<PanGestureHandler
onHandlerStateChange={this.onPanHandlerStateChange}
onGestureEvent={this._onHandleGesture}
failOffsetX={[-xThreshold, xThreshold]}
>
<Animated.View
style={{
position: "absolute",
left: 0,
top: 0,
transform: [{ translateX: this.state.animationValue.x }, { translateY: this.state.animationValue.y }],
}}
>
<CardTile size={size} content={content} layout={layout} backgroundImage={backgroundImage} shadow={shadow} />
</Animated.View>
</PanGestureHandler>
This example is based on the react-native-gesture-handler library, but the concept should apply to other solutions.
Dont know if this way is advisable, though it is functional.
Hope this helps!

Scale stage and drag element relative positions not working

Im having small issue with calculating realtime position while scaled stage in KonvaJS React. In my example I have two Rect's small (red) one position is relative to Big Rect (yellow), While dragging big rectangle small one moves relative to big one but when I scale UP or DOWN small one jumps some pixels in x, y positions.
Working example
import React from "react";
import ReactDOM from "react-dom";
import { Rect, Stage, Layer } from "react-konva";
import "./styles.css";
function Box({ attrs, draggable, updateAttr }) {
const onDragStart = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
const onDragMove = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
const onDragEnd = e => {
const element = e.target;
if (typeof updateAttr === "function") {
updateAttr(element._lastPos);
}
};
return (
<Rect
draggable={draggable}
onDragEnd={onDragEnd}
onDragMove={onDragMove}
onDragStart={onDragStart}
fill={attrs.fill}
x={attrs.x}
y={attrs.y}
width={attrs.width}
height={attrs.height}
/>
);
}
const calculatePos = ({ r1, r2 }) => ({
...r2,
x: parseInt(r1.width + r1.x + 10, 10),
y: parseInt(r1.y + r1.height / 2 - r2.height / 2, 10)
});
const boxAttr = {
x: 50,
y: 50,
width: 200,
height: 150,
fill: "yellow"
};
const secondAttr = calculatePos({
r2: {
fill: "red",
width: 20,
height: 20
},
r1: boxAttr
});
class App extends React.Component {
state = {
scale: 1,
boxAttr,
secondAttr
};
updateScale = scale => {
this.setState({ scale });
};
updateAttr = pos => {
const { secondAttr, boxAttr } = this.state;
this.setState({
secondAttr: calculatePos({
r2: secondAttr,
r1: { ...boxAttr, ...pos }
})
});
};
render() {
const { scale, boxAttr, secondAttr } = this.state;
return (
<div className="App">
<button
onClick={() => {
this.updateScale((parseFloat(scale) + 0.1).toFixed(1));
}}
>
+ Scale ({scale})
</button>
<button
onClick={() => {
this.updateScale((parseFloat(scale) - 0.1).toFixed(1));
}}
>
- Scale ({scale})
</button>
<Stage
scaleX={scale}
scaleY={scale}
width={window.innerWidth}
height={window.innerHeight}
>
<Layer>
<Box
updateAttr={this.updateAttr}
draggable={true}
attrs={boxAttr}
/>
<Box draggable={false} attrs={secondAttr} />
</Layer>
</Stage>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Please help me to update correct x,y for red rectangle.
I know Grouped Draggable solution will fix this issue, but I cannot
implement in my real application due to complex relations that cannot
be group for draggable reason
When the first box is dragged, you have to translate the mouse coordinates on the canvas to the coordinate system inside the scaled canvas to move the second box.
I modified the updateAttr method to do so.
There is a catch however, when the updateAttr is called by konva itself (after the dragEnd event) it does so with translated coordinates. That is why I added a second argument to the updateAttr called doScale. When calling it yourself, set it true and it will translate the coordinates, if konva calls it, there is no second argument and it evaluates to false and does no translation.
The code is available at: https://codesandbox.io/s/6126wz0kkk

React Native Animated PanResponder reverse on second input

I got a component using the PanResponder in combination with Animated from React Native API's. The code of my component:
import React, { Component } from 'react';
import { Animated, PanResponder } from 'react-native';
import { SVG } from '../';
import { Icon, LockContainer, StatusCircle } from './styled';
class VehicleLock extends Component {
state = {
pan: new Animated.ValueXY({ x: 9, y: 16 }),
};
componentWillMount() {
this.animatedValueY = 0;
this.minYValue = 16;
this.maxYValue = 175;
this.state.pan.y.addListener((value) => {
this.animatedValueY = value.value;
});
this.panResponder = PanResponder.create({
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (evt, gestureState) => {
this.state.pan.setOffset({ x: 0, y: 0 });
// this.state.pan.setOffset(this.state.pan.__getValue());
this.state.pan.setValue({ x: 9, y: this.minYValue });
},
onPanResponderMove: (evt, gestureState) => {
// deltaY: amount of pixels moved vertically since the beginning of the gesture
let newY = gestureState.dy;
if (newY < this.minYValue) {
newY = this.minYValue;
} else if (newY > this.maxYValue) {
newY = this.maxYValue;
}
Animated.event([null, {
dy: this.state.pan.y,
}])(evt, {
dy: newY,
});
},
onPanResponderRelease: (evt, gestureState) => {
let newY = this.minYValue;
const releaseY = gestureState.dy;
if (releaseY > 83) {
newY = this.maxYValue;
}
Animated.spring(this.state.pan, {
toValue: {
x: 9,
y: newY,
},
}).start();
},
});
}
componentWillUnmount() {
this.state.pan.x.removeAllListeners();
this.state.pan.y.removeAllListeners();
}
render() {
const customStyles = {
...this.state.pan.getLayout(),
position: 'absolute',
zIndex: 10,
transform: [
{
rotate: this.state.pan.y.interpolate({
inputRange: [this.minYValue, this.maxYValue],
outputRange: ['0deg', '180deg'],
}),
},
],
};
return (
<LockContainer>
<SVG icon="lock_open" width={16} height={21} />
<Animated.View
{...this.panResponder.panHandlers}
style={customStyles}
>
<StatusCircle>
<Icon>
<SVG icon="arrow_down" width={23} height={23} />
</Icon>
</StatusCircle>
</Animated.View>
<SVG icon="lock_closed" width={16} height={21} />
</LockContainer>
);
}
}
export default VehicleLock;
As you can see in my code I animate the Y value with a boundary. It has to stay in a box between certain values. As soon users release the drag and it's over half of the max Y value it's animated to the max value.
This works without any problems, but on the second interaction I would like to reverse the action. So instead of going down, it has to go up. Unfortunately on release the Y value resets.
As you can see in the comment in my code I know the movement is based on the delta, so the moved Y value since the interaction started. This is explained in this great comment PanResponder snaps Animated.View back to original position on second drag.
Although I don't know how to fix it. Here you can see my current behaviour:
On the second input the element snap back to the top. Which is expected behavior. As #jevakallio states in his comment, you can reset the values in the offset in onPanResponderGrant. As I do that (commented out), the element resets the values, but on second interaction it will animate outside the container. So in that case 0 is the maxYValue and it animates 175 outside the container to the bottom.
How can I make a reversed animated outside back to the top? I don't seem to get this. Thanks in advance!
According to Panresponder docs the onPanResponderGrant should indicate the start of a gesture. So if you set the this.state.pan.setOffset and the this.state.pan.setValue at this point, it will reset with the start of every gesture. Try a console.log in the onPanResponderGrant and see what happens.
Also, a parent or wrapping component implementing the PanResponder could be interfering with your PanResponder. This could be another good starting point to troubleshoot.
From the PanResponder docs:
onPanResponderGrant: (evt, gestureState) => {
// The gesture has started. Show visual feedback so the user knows
// what is happening!
// gestureState.d{x,y} will be set to zero now
},

Categories