React Native how to rotate and translate at the same time - react-native

How do you rotate and translate at the same time?
In the sample below I was expecting the square to move to right and rotate about its own axis maintaining the same central y axis. If you can imagine a wheel travelling along a road
Instead, it flys off wildly
Any help would be appreciated
import React, { Component } from "react";
import {
AppRegistry,
StyleSheet,
Text,
View,
Animated
} from "react-native";
class App extends Component {
componentWillMount () {
this._animatedValue = new Animated.Value(0);
}
componentDidMount () {
Animated.timing(this._animatedValue, {
toValue: 100,
duration: 3000
}).start();
}
render () {
const interpolatedRotateAnimation = this._animatedValue.interpolate({
inputRange: [0, 100],
outputRange: ['0deg', '360deg']
});
const interpolatedRotateX = this._animatedValue.interpolate({
inputRange: [0, 100],
outputRange: [0, 200]
});
return (
<View style={styles.container}>
<Animated.View
style={[styles.box, {transform: [
{rotate: interpolatedRotateAnimation},
{translateX:interpolatedRotateX}
]}
]}
/>
</View>
);
}
};
const styles = StyleSheet.create({
container: {
flex: 1
},
box: {
backgroundColor: 'red',
position: 'absolute',
top: 100,
left: 100,
width: 100,
height: 100
}
});
export default App

What I was missing was that the axis was the center of the object.
The translate is relative to axis of the own object which is changing due to the rotation.
Translate x/y is not simply moving the object relative to the screen but relative to its own axis.

Related

How to create a shaking/floating animation to an image (react native)

I want to create a shaking/floating (call it what you want) animation to an image.
Basically the image will float or wiggle left to right and at the same time up and down.
The animation will be triggered automatically.
This is the component I have. Image is a round ball so nothing fancy.
I tried putting values in inputRange and outputRange but nothing happened. Probably because it doesn't trigger the animation (?).
New to react native, not even sure if below is a good start to my animationgoal.
import React, { Component } from "react";
import { Animated } from "react-native";
import Images from "./assets/Images";
export default class Round extends Component {
constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
}
render() {
let float = this.animatedValue.interpolate({
inputRange: [],
outputRange: [],
});
return (
<Animated.Image
style={{
position: "absolute",
left: x,
top: y,
width: width,
height: height,
transform: { translate: float },
}}
resizeMode="stretch"
source={Images.round}
/>
);
}
}
I used the react-native-animatable library to solve my problem
I have created something similar to iPhone delete app screen, where all icons wiggle.
You can decrease duration field to increase the speed of wiggle.
const spinValue = new Animated.Value(0);
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(spinValue, {
toValue: 1,
duration: 30,
useNativeDriver: true,
}),
Animated.timing(spinValue, {
toValue: 2,
duration: 40,
useNativeDriver: true,
}),
]),
).start();
}, [spinValue]);
const spin = spinValue.interpolate({
inputRange: [0, 1, 2],
outputRange: ['0deg', '2deg', '-2deg'],
});
**
JSX **
<
Animated.View
style = {
[
styles.container,
styles.deleteContainer,
{
transform: [{
rotate: spin
}],
},
]
} >
<
Image
style = {
[styles.thumb, styles.deleteContainer]
}
source = {
{
uri: item.thumbnailUrl
}
}
/> <
/Animated.View>
deleteContainer: {
opacity: 0.85,
borderRadius: 5,
borderColor: 'red',
borderWidth: 1.4,
},

Error while animating rotateX in react native

I am trying to rotate an icon on press of a button in my react native application.
But I am getting this error:
Error while updating property 'transform' of a view managed by:
RCTView
This is the Icon itself:
<TouchableOpacity
style={[
styles.expandButton,
{transform: [{rotateX: toString(expandIconAngle) + 'deg'}]},
]}
onPress={() => {
rotateIcon();
}}>
<Icon name="expand-less" color="#ffffff" size={28} />
</TouchableOpacity>
This is the function which is responsible for rotating the icon:
const expandIconAngle = useRef(new Animated.Value(0)).current;
function rotateIcon() {
Animated.timing(expandIconAngle, {
toValue: 180,
duration: 300,
easing: Easing.linear,
}).start();
}
Where am I going wrong?
use interpolate and Animated.Image :
import React, { useRef } from "react";
import { Animated, Text, View, StyleSheet, Button, SafeAreaView,Easing,TouchableOpacity } from "react-native";
const App = () => {
// fadeAnim will be used as the value for opacity. Initial Value: 0
const angle = useRef(new Animated.Value(0)).current;
const fadeOut = () => {
// Will change fadeAnim value to 0 in 3 seconds
Animated.timing(
angle,
{
toValue: 1,
duration: 3000,
easing: Easing.linear, // Easing is an additional import from react-native
useNativeDriver: true // To make use of native driver for performance
}
).start()
};
const spin =angle.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
return (
<SafeAreaView style={styles.container}>
<Animated.Image
style={{transform: [{rotateX: spin}] }}
source={require('#expo/snack-static/react-native-logo.png')} />
<TouchableOpacity onPress={fadeOut} style={styles.buttonRow}>
<Text>Button</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
buttonRow: {
flexBasis: 100,
justifyContent: "space-evenly",
marginVertical: 16
}
});
export default App;
LIVE example - https://snack.expo.dev/TP-fQExXT

framedrop on real device, not on simulator (IOS) - React Native Reanimated

I have the following code to create a 'dynamic' textinput. A label is placed above the textinput. When the textinput is focused, the label moves above the textinput and the fontsize and color changes. On blur of the textinput, the label moves back (if there is no input in the textinput).
This code contains react-native-reanimated code, should animations should work on the UI thread, not the JS thread.
When I test this code on an IOS simulator (tested iPhone 7 -> iPhone 11 Pro) and I focus on a textinput so the animation runs, the JS thread drops about 3-6 frames (54-57 fps), which is OK I guess. On a real device (iPhone 7), the JS thread drops about 20-30 frames (and sometimes even more). When I start typing in the textinput, I get a slow callback (I check the input of the textinput while typing). On the simulator, the callback is immediately. On the real device, it sometimes takes up to 2 seconds before the input is checked.
import React, { useState, useEffect } from 'react';
import { View, TextInput } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { interpolateColor, useTimingTransition } from 'react-native-redash';
import Colors from '../../constants/Colors';
const { interpolate } = Animated;
const AuthenticationInput = React.forwardRef((props, ref) => {
const {
ref,
label,
onChangeText,
secureTextEntry,
returnKeyType,
icon
} = props;
const [value, setValue] = useState('');
const [trans, setTrans] = useState(0);
const transition = useTimingTransition(trans, {
duration: 250,
easing: Easing.inOut(Easing.ease)
});
// move the label in the x direction
const moveX = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, 0],
extrapolate: Extrapolate.CLAMP
});
// move the label in the y direction
const moveY = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, -20],
extrapolate: Extrapolate.CLAMP
});
// change the font size of the label
const fontSize = interpolate(transition, {
inputRange: [0, 1],
outputRange: [15, 12.5],
extrapolate: Extrapolate.CLAMP
});
// change the color of the label
const color = interpolateColor(transition, {
inputRange: [0, 1],
outputRange: ['#aaa', '#000']
});
// pass the input of the textinput to the
// onChangeText function passed as a prop
useEffect(() => {
onChangeText(value)
}, [value]);
return (
<View style={{
marginHorizontal: 25,
marginTop: 25,
borderRadius: 5,
backgroundColor: Colors.white
}}
>
<Animated.View style={{
position: 'absolute',
transform: [{ translateX: moveX }, { translateY: moveY }]
}}
>
<Animated.Text style={{
fontSize,
color
}}
>
{label}
</Animated.Text>
</Animated.View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TextInput
ref={ref}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
secureTextEntry={secureTextEntry}
onChangeText={(v) => setValue(v)}
style={{
margin: 0,
padding: 12.5,
flex: 1,
fontSize: 15
}}
hitSlop={{
top: 10,
right: 10,
bottom: 10,
left: 10
}}
onFocus={() => setTrans(1)}
onBlur={() => {
if (value.length === 0) {
setTrans(0);
}
}}
returnKeyType={returnKeyType}
/>
<View style={{ padding: 12.5 }}>
{icon}
</View>
</View>
</View>
);
});
export default AuthenticationInput;

React Native - Touchable Opacity inside Animated.View is firing event of background list view

I Have a list with scroll view, I am trying to add filter options for it. When click on a filter icon, an overlay with position:absolute will be displayed inside a Animated.View. I have Buttons inside overlay View with TouchableOpacity
Filter.js
export default class FilterFade extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
};
componentWillMount() {
this._visibility = new Animated.Value(this.props.visible ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
if (nextProps.visible) {
this.setState({ visible: true });
}
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
}).start(() => {
this.setState({ visible: nextProps.visible });
});
}
render() {
const { visible, style, children, ...rest } = this.props;
const containerStyle = {
opacity: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
scale: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [1.1, 1],
}),
},
],
};
const combinedStyle = [containerStyle, style];
return (
<Animated.View style={combinedStyle} {...rest}>
{children}
</Animated.View>
);
}
}
View.js
<FilterFade visible={this.state.isFilterVisible}>
<View style={styles.filterView}>
<TouchableOpacity onPress={() => this.getFilteedStories}>
<Text style={styles.filterOption}> My Stories </Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.getFilteedStories}>
<Text style={styles.filterOption}> All Stories </Text>
</TouchableOpacity>
</View>
</FilterFade>
Styles
filterView :{
position: 'absolute',
top: 0,
right: 5,
backgroundColor: #CCC,
width: 150,
paddingTop: 15,
paddingBottom: 15,
zIndex: 999,
},
filterOption: {
color: "#FFF",
fontSize: 15
}
Now, When I click on TouchableOpacity Text in Filter, the click event is triggered in Listview which is behind the FadeView.
Can Some one please let me know on how to add press event inside a Animated absolute view.
Thanks in Advance.
Use TouchableOpacity from 'react-native-gesture-handler' instead of from 'react-native'.
import { TouchableOpacity } from 'react-native-gesture-handler';
Follow this post Cannot click TouchableOpacity in animated.view using React native

React native animation of a View from side to side

I'm working with the animation of react-native lately and I'm trying to make a View component moving from side to side by click.
This is the code I have so far that works :
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Animated,
Easing,
TouchableOpacity,
Dimensions
} from 'react-native';
export default class App extends Component {
constructor () {
super()
this.state = {
isLeftSide: true,
}
this.animatedValue = new Animated.Value(0)
}
animate () {
this.animatedValue.setValue(0);
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 300,
easing: Easing.linear
}
).start()
}
fire = () => {
this.animate();
this.setState({isLeftSide: (!this.state.isLeftSide)});
}
direction = () => this.state.isLeftSide ? 'rtl' : 'ltr';
render() {
const screenWidth = Dimensions.get('screen').width;
const objectMaxCoord = screenWidth - 40;
const outputRange = {
rtl: [0, objectMaxCoord],
ltr: [objectMaxCoord, 0]
}
const marginLeft = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: outputRange[this.direction()]
})
return (
<View style={styles.container}>
<TouchableOpacity onPress={() => this.fire()}><Text>Run</Text></TouchableOpacity>
<Animated.View
style={{
marginLeft,
height: 30,
width: 40,
backgroundColor: 'red'}} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
marginTop: 30
}
});
Now, the problem is that the red cube starts on the left side, but as I click run, it jumps (without animation) to the right corner and then move smoothly(in animation) to the left side. what comes after that works just perfect. Why is this first jump happens ?
And is there any easier way to make this animation done ?
( P.S I'm working on android )
Here is a link to an expo
https://snack.expo.io/S1aAgNq6Z
Probably because the state might not update right away. Try to change your fire function like this:
this.setState({isLeftSide: (!this.state.isLeftSide)}, () => {
this.animate();
});
setState accepts a callback when done.
I was playing with a Snack for a while and the issue is clearly that the state gets unsync because it says is on the left side when actually is on the right. I don't see anything else messing with the state.
You can see it here: https://snack.expo.io/BJhb4-pAW
The snack is somehow limited so can you try this on your code?
finishedAnimation = (finished) => {
if (finished)
this.setState({isLeftSide: !(this.state.isLeftSide)});
}
fire = () => {
this.animatedValue.setValue(0);
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 300,
easing: Easing.linear
}
).start(this.finishedAnimation);
}