Getting measurements of UI elements in React Native - react-native

Is there a way to get pixel measurements of native elements in React Native? For example:
Right now I'm hardcoding how much padding needs to exist so that the content isn't covered by the nav bar:
const styles = StyleSheet.create({
container: {
paddingTop: 64
}
});
IMO this is not acceptable. Is there some way to measure these elements?

Yes, you can use the onLayout event:
getInitialState() {
return { }
}
<View onLayout={(event) => this.measureView(event)}>
measureView(event) {
console.log('event properties: ', event);
this.setState({
x: event.nativeEvent.layout.x,
y: event.nativeEvent.layout.y,
width: event.nativeEvent.layout.width,
height: event.nativeEvent.layout.height
})
}
As far as calling these on Native elements, I have not tried, but possibly passing the function into the component may do it, or wrapping the native element in a view and calling the function on the outer view.

Related

React Native animate position (Left & Right) using useNativeDriver

Is it possible to extend ReactNative to allow animation of an element's position using the native driver.
Whilst you can animate via translateX, in this case, the size of the element is to be reduced adding by changing both its left and right style parameters.
Neither left, right, marginLeft, marginRight, paddingLeft nor paddingRight are supported for native animations. Is there a way around this or some ingenious idea involving scaling that won't distort the element.
export class ViewScreen extends React.Component {
constructor(props) {
super(props);
this.state= {
scrollY: new Animated.Value(0)
}
}
render() {
var VSStickyElementsMargin = this.state.scrollY.interpolate({
inputRange: [0, 44],
outputRange: [0, 4]
});
return (
<Animated.ScrollView
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.state.scrollY } }}],
{ useNativeDriver: true }
)}>
<Animated.View style={[
{left: VSStickyElementsMargin},
{right: VSStickyElementsMargin}
]}>
<Text>I am content that is not to become distorted</Text>
</Animated.View>
</Animated.ScrollView>
)
}
}
There is no way to use native driver on properties that are not transforms or opacity.
If I understand what you are trying to achieve correctly you should use use translateX to move the element outwith its parent and animate it back into view when you need to, use overflow hidden on the parent.
Alternatively if this is not the effect you want try looking at the layoutanimation api as you can get better performance using it to drive values than animating non transform/opacity properties.

How can I implement animation in my flatlist?

I am using Flatlist in my rn project and when I push new data into my flatlist, my item 1 will automatically move from position A to position B. But my question is I don't want it to just change the position, I want to use animation to move my item(from position A to position B). How can I implement that? Thank you!
Please check the demo picture and video from the link down below:
https://photos.app.goo.gl/WypswNyA38A2EAPQA
https://photos.app.goo.gl/Ev1RYMduDj7mxrHn7
You can use Animated component to do the animation. As per your attached video, 2 steps animation comes into play, one which pushes the items up in the list and another one which increases the opacity of the list item. A simple approach would be to add the list item with height 0 and increase the height to desired height using animation, this will complete the first step. Once the first step is completed, control the opacity to go from 0 to 1.
Next, you need to start the animation when the list item is added to the list, componentDidMount is the right place to do so. Please consider the following component which does the above steps.
import React from 'react';
import { Animated } from 'react-native';
class AnimatedListItem extends React.Component {
constructor(...props) {
super(...props);
this.state = {
height: new Animated.Value(0),
opacity: new Animated.Value(0)
};
}
componentDidMount() {
Animated.sequence([
Animated.timing(
this.state.height,
{
toValue: this.props.height,
duration: this.props.duration || 1000
}
),
Animated.timing(
this.state.opacity,
{
toValue: 1,
duration: this.props.duration || 1000
}
)
]).start();
}
render() {
const { height, opacity } = this.state;
return (
<Animated.View
style={{
...this.props.style,
height: height,
opacity: opacity
}}
>
{this.props.children}
</Animated.View>
);
}
}
export default AnimatedListItem;
In the above snippet, two animations are passed to Animated.sequence([...]) method to animate one after the other.
You can now use the above component in the renderItem method like
renderItem = () => {
return (
<AnimatedListItem height={50} duration={800} style={...}>
/* render actual component here */
</AnimatedListItem>
);
}
Hope this will help!
Note: This is a bare minimum example to achieve what you are looking for.

How to handle responsive layout in React Native

I'm using the react-native-dimension library for making my UI responsive as follows:
const{width,height} = Dimensions.get('window');
and in my style.js file :
imageBackgroundLandscape:{
width:height,
height:width
},
imageBackgroundPortrait:{
width:width,
height:height
}
The problem is that when I rotate the screen, the width and height variables have got previous values!
For example in the portrait mode my variables are:
width : 800
height: 1280
and when I rotate the screen my variables are:
width : 800 // previous value
height: 1280 // previous value
In addition, I use the react-native-orientation to determine the mode of the screen.
I want to know how can I change the values of them (width, height) automatically when I rotate the device, or are there any other libraries for this?
Thanks in advance.
I usually handle the height, width confusion with the following code:
//Dimensions.js
import {Dimensions} from 'react-native';
const {height, width} = Dimensions.get('window');
const actualDimensions = {
height: (height<width) ? width : height,
width: (width>height) ? height : width
};
export default actualDimensions;
Instead of requiring the height and width from Dimensions, use the actualDimensions and for managing the orientation gracefully you should give a try to this library as well.
The Dimensions are loaded before the JS bundle gets loaded into the app so it is recommended to fetch the height, width dynamically for every render
You can read this here
I usually used Flexbox to arrange the layout for my components. It helps them to be responsive. Maybe you could give a try too.
Layout with Flexbox
You can use these steps to make your UI responsive.
1: use percentage whenever it's possible
2: use the power of flexbox to make your UI grow and shrink
3: use Dimension API
Actually, you do right but half of the task. you got the width and height from Dimensions and it is right, but how react-native understand your orientation changes?
First, your code should understand the change of orientation, then you set a call-back function to change the state of your application for implementing new width and height.
Awfully, I don't know the react-native can understand a change of orientation with its built-in functions or not. So I'm using this library to understand orientation changes and then I use setState to re-render the codes.
Absolutely, I put the width and height inside state of the component.
If you wanna lock the orientation change, use this library.
Firstly:
You are facing that issue is because you forgot to call const{width,height}
= Dimensions.get('window'); again when the orientation has changed.
In order to get the latest value of width and height after the orientation change you would have to call the Dimensions.get('window') function again and get width and height from it's output.
Secondly:
Instead of using multiple libraries, you can just use one library(react-native-styleman), that lets you handle this type of stuff very easily:
Here is how the code would look like using react-native-styleman.
import { withStyles } from 'react-native-styleman';
const styles = () => ({
container: {
// your common styles here for container node.
flex: 1,
// lets write a media query to change background color automatically based on the device's orientation
'#media': [
{
orientation: 'landscape', // for landscape
styles: { // apply following styles
// these styles would be applied when the device is in landscape
// mode.
backgroundColor: 'green'
//.... more landscape related styles here...
}
},
{
orientation: 'portrait', // for portrait
styles: { // apply folllowing styles
// these styles would be applied when the device is in portrait
// mode.
backgroundColor: 'red'
//.... more protrait related styles here...
}
}
]
}
});
let MainComponent = ({ styles })=>(
<View style={styles.container}>
<Text> Hello World </Text>
</View>
);
// now, lets wire up things together.
MainComponent = withStyles(styles)(MainComponent);
export {
MainComponent
};
I am using react-native-responsive-screen. it is working also with orientation change
USAGE
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
listenOrientationChange as lor,
removeOrientationListener as rol
} from 'react-native-responsive-screen';
class Login extends Component {
componentDidMount() {
lor(this);
}
componentWillUnmount() {
rol();
}
render() {
const styles = StyleSheet.create({
container: { flex: 1 },
textWrapper: {
height: hp('70%'),
width: wp('80%')
},
myText: { fontSize: hp('5%') }
});
return (
<View style={styles.container}>
<View style={styles.textWrapper}>
<Text style={styles.myText}>Login</Text>
</View>
</View>
);
}
}
export default Login;

New animation after setState - React Native

I don't find the answer to my problem for my react native app.
If you have an idea how to achieve that, that would be great :)
What I'm trying to do:
In a page, when I press somewhere, I want to display an animation (for example a square apparition) on the press position.
What I have achieved:
When I click, a square is display with an animation on the right position.
But when i click somewhere else, The position of the square changes but the animation doesn't restart.
What I have tried:
To do the animation, I place a < View/> (with position: 'absolute') on the press position.
This < View/> is embeded in a component that I call 1 time in my App render:
<ClickAnimation x={item.x} y={item.y}/>
where item.x and item.y are are the coordinate.
This is the code of my component:
import React from 'react';
import {Animated, View} from 'react-native';
export default class ClickAnimation extends React.Component {
state = {
scaleAnim: new Animated.Value(0)
};
componentWillMount() {
Animated
.timing(this.state.scaleAnim, {
toValue: 2,
duration: 500
})
.start();
}
componentWillUpdate(nextProps) {
if (nextProps.x != this.props.x && nextProps.y != this.props.y) {
this.setState({
scaleAnim: new Animated.Value(0)
})
}
}
componentDidUpdate() {
console.log("componentDidUpdate",this.state.scaleAnim)
Animated
.timing(this.state.scaleAnim, {
toValue: 2,
duration: 500
})
.start();
}
render() {
return (<Animated.View
style={{
position: "absolute",
top: this.props.y,
left: this.props.x,
width: 50,
height: 50,
backgroundColor: "red",
transform: [
{
scaleY: this.state.scaleAnim
}, {
scaleX: this.state.scaleAnim
}, {
translateX: -25
}, {
translateY: -25
}
]
}}/>);
}
}
The console.log in componentDidUpdate give me for each click 2 logs:
{_children: Array(2), _value: 2, ..., _animation: null…}
{_children: Array(2), _value: 0,..., _animation: null…}
I really don't know what to do next.
PS: In NativeScript, that was more easy. I had just to add the new component to the DOM.
According to React docs you cannot this.setState() inside componentWillUpdate(),if you need to update state in response to a prop change, use componentWillReceiveProps(nextProps) instead.
https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops
Read the above link for more details on that and check its caveats.
I hope this is what is causing the problem
It seems that EXPO XDE make the application too slow and this is why the animation part doesn't work properly.
I have found the solution.
This come with this issue:
https://github.com/facebook/react-native/issues/6278
I had seen it and this is why I wrote first 0,001. But 0,001 is still to little. With 0,01 it works great.
So the answer is:
Just replace 0 by 0.01 because it was too little.

React Native how to do min/max widths

I want to style a component in my interface. The component must have a width of at least 200, but I want to let it grow with screen width to up to 600. But, sometimes people use tablets or huge phones. And I don't want the component to be able to grow with the screen forever. I want it to have a maximum width of 600.
And I know maxWidth is a thing that is, at least for now, not a part of the flexbox implementation in React Native... so, is there a reasonable way to do this today?
You can use the maxWidth, maxHeight, minWidth, minHeight layout properties supported by React Native.
Document here React Native layout props.
Example:
StyleSheet.create({
container: {
maxWidth: '80%', // <-- Max width is 80%
minHeight: 20, // <-- Min height is 20
},
});
There is no such thing as "maxWidth" in React Native. You may want to style your component at run-time. Try playing with Dimensions. You can get screen width and screen height of the device and adjust width of your component accordingly.
You can define two different style objects.
For full-width component on a device having width less than 600.
componentStyle_1: {
flex: 1
}
For 600 width on a device having width greater than 600
componentStyle_2: {
width: 600
}
You can check the device width runtime.
var {height, width} = Dimensions.get('window');
if(width>600){
//load componentStyle_1
}
else{
//load componentStyle_2
}
Best way to get accurate results is to play with your code. Good luck!
Refer: https://facebook.github.io/react-native/docs/dimensions.html#content
Simple. Just use maxWidth in your styles.
In a practical manner, this is how you would use it:
import { StyleSheet, Text, View, Dimensions, (+ anything else you need such as Platform to target specific device widths } from "react-native";
// plus whatever other imports you need for your project...
In a class component, you would create a state called whatever, let's say deviceWidth. Then inside the component you would use:
constructor(props) {
super(props);
this.state = {
deviceWidth: 375, // Put in any default size here or just "null"
// plus any other state keys you need in your project
}
componentDidMount() {
const currentWidth = Dimensions.get("screen").width;
this.setState({deviceWidth: currentWidth});
}
In a functional component you would import:
import React, { useEffect, useState } from "react";
Then inside your functional component you would add in:
const [currentWidth, setCurrentWidth] = useState(null //or add in a default width);
useEffect(() => {
const currentWidth = Dimensions.get("screen").width;
setCurrentWidth({deviceWidth: currentWidth});
}, []);
You could also use:
const deviceDisplay = Dimensions.get("window");
const deviceHeight = deviceDisplay.height;
const deviceWidth = deviceDisplay.width;
..if you wanted to find the height as well. On Android, "window" gets you the full screen height including the upper bar while "screen" gets you height without upper bar. Window and screen on iOS are the same.
Then using inline styles so that you have access to the state, set the width & maxWidth:
<View style={[styles.wrapper, { width: this.state.deviceWidth, maxWidth: 400, // or whatever you want here. } ]} >
Any width settings in a wrapper style found in your StyleSheet object will be over-ridden by the inline style, just like in CSS.
Alternatively if you don't have any other styles declared in your StyleSheet object, just use:
<View style={{ width: this.state.deviceWidth, maxWidth: 400 }} >
Or in a functional component, that would be:
<View style={{ width: deviceWidth, maxWidth: 400 }} >