React Native animate position (Left & Right) using useNativeDriver - react-native

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.

Related

Is it possible to use animated setValue with useNativeDriver?

I am translating views to follow a move gesture, with least possible latency.
I am investigating a couple of options and one of which is - if possible - to send the setValue action to UI thread, rather than the JS thread.
Even though in the documentations it is stated that the Animated.timing, Animated.spring, etc type of functions can be offloaded to the UI thread with useNativeDriver: true directive, there is no reference - at least that I could have found - on if it is possible to send the direct setValue action to the UI thread or not.
My question is; is it possible to use animated setValue with useNativeDriver and how if possible.
Below is the rough example of the code that I am using:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.spaceAnimatedTranslations = new Animated.ValueXY();
this._animatedStyle = {
transform: [
{ translateX: this.spaceAnimatedTranslations.x },
{ translateY: this.spaceAnimatedTranslations.y }
]
};
}
onSpaceMove(event) {
this.spaceAnimatedTranslations.setValue({x: event.nativeEvent.translationX, y: event.nativeEvent.translationY});
}
render(){
return <PanGestureHandler
key={`test`}
onGestureEvent={e => this.onSpaceMove(e)}>
<Animated.View
ref={ref => {
this.testAnimatedView = ref;
}}
style={[this._animatedStyle]}>
<View style={styles._box_content}>
{someContent}
</View>
</Animated.View>
</PanGestureHandler>
}
}
You have to use Animated.event to do the mapping directly in native thread. See Tracking gestures. Other possibility would be to use react-native-reanimated with react-native-gesture-handler.

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;

ScrollView onScroll event

react-native: 0.50.3
VIDEO: https://youtu.be/Piz30mH4o1s
I want to hide or show navbar on scroll. When user scrolls down, I change the height of navbar and 'paddingTop' position of content. After that 'onScroll' event triggering several times, even if user didn't scroll.
How can I deal with this?
I will show u a similar exemple ı hope it helps:
I assume that you have header component and mainPage components. In mainPage you can pass scrollY value to the header component with onScroll method like this:
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {
y:
this.refs.header === undefined
? 0
: this.refs.header.state.scrollY
}
}
}
])}
And than in header component, you can interpolate your your scrollY value to change headerHeight or header padding style like this
const headerHeight = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT],
extrapolate: "clamp"
});
Than u have to change style your animated.view like this:
<Animated.View style={[styles.someStyle, {height: headerHeight}]}></Animated.View>
Feel free to ask any question to me.

Getting measurements of UI elements in 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.