Can not find Variable in react-native - javascript

I am trying to build an app in react native and keep having an error titled Cannot find variable: opacity in a feature that should fade an image in and then fade an image out depending on the user pressing a button.
I have tried multiple things such as using the let operator and trying to change where the code is located in the class.
here is the code for the button and animation
import React, { Component } from "react";
import {
Image,
Animated,
Easing,
StyleSheet,
TouchableWithoutFeedBack
} from "react-native";
const styles = StyleSheet.create({
info: {
// for info button
height: 50,
width: 50,
flex: 1,
position: "absolute",
left: 315,
bottom: -675
}
});
class Info extends Component<Props> {
state = { opacity: new Animated.Value(0) };
infoScale = opacity.interpolate({
inputRange: [0, 1],
outputRange: [0.85, 1],
});
transformStyle = {
...styles.image,
transform: [{ opacity: infoScale }]
};
render() {
return (
<TouchableWithoutFeedBack
onPressIn={() => {
scaleValue.setValue(0);
Animated.timing(opacity, {
toValue: 1,
duration: 250,
easing: Easing.linear,
useNativeDriver: true
}).start();
}}
onPressOut={() => {
Animated.timing(opacity, {
toValue: 0,
duration: 100,
easing: Easing.linear,
useNativeDriver: true
}).start();
}}
>
<Image
source={require("../assets/images/info2.png")}
style={styles.info}
resizeMode="contain"
/>
<Animated.View style={transformStyle}>
<Image source={require("../assets/images/iPhoneTimeTreeInfo.png")} />
</Animated.View>
</TouchableWithoutFeedBack>
);
}
}
export default Info;

opacity in Animated.timing should be this.state.opacity
import React, { Component } from "react";
import {
Image,
Animated,
Easing,
StyleSheet,
TouchableWithoutFeedBack
} from "react-native";
const styles = StyleSheet.create({
info: {
// for info button
height: 50,
width: 50,
flex: 1,
position: "absolute",
left: 315,
bottom: -675
}
});
class Info extends Component<Props> {
state = { opacity: new Animated.Value(0) };
infoScale = this.state.opacity.interpolate({
inputRange: [0, 1],
outputRange: [0.85, 1],
});
transformStyle = {
...styles.image,
transform: [{ opacity: infoScale }]
};
render() {
return (
<TouchableWithoutFeedBack
onPressIn={() => {
scaleValue.setValue(0);
Animated.timing(this.state.opacity, {
toValue: 1,
duration: 250,
easing: Easing.linear,
useNativeDriver: true
}).start();
}}
onPressOut={() => {
Animated.timing(this.state.opacity, {
toValue: 0,
duration: 100,
easing: Easing.linear,
useNativeDriver: true
}).start();
}}
>
<Image
source={require("../assets/images/info2.png")}
style={styles.info}
resizeMode="contain"
/>
<Animated.View style={transformStyle}>
<Image source={require("../assets/images/iPhoneTimeTreeInfo.png")} />
</Animated.View>
</TouchableWithoutFeedBack>
);
}
}
export default Info;

Related

How can I use the rotation gesture handler to rotate an animated view in react native about a specific point (not its center)?

I have used the below model to make a handler rotate but cannot find a way to manipulate it as to make the handle rotate from where it begins instead of its center. As well, I am struggling to make the rotation occur with only one finger instead of two.
I think maybe the answer lies in the withAnchorPoint library but Iā€™m not sure of how to apply it.
Requiring assistance šŸ™šŸ¾
import React from 'react';
import {Animated, StatusBar, Image, ImageBackground, Dimensions, StyleSheet, View, Text} from "react-native";
import { PanGestureHandler, PinchGestureHandler, State, RotationGestureHandler } from 'react-native-gesture-handler';
//import { withAnchorPoint } from 'react-native-anchor-point';
import { USE_NATIVE_DRIVER } from '../../config';
const {width} = Dimensions.get('screen');
const SIZE = width * .9;
export default class MovableClock extends React.Component {
panRef = React.createRef();
rotationRef = React.createRef();
pinchRef = React.createRef();
constructor(props) {
super(props);
/* Pinching */
this._baseScale = new Animated.Value(1);
this._pinchScale = new Animated.Value(1);
this._scale = Animated.multiply(this._baseScale, this._pinchScale);
this._lastScale = 1;
this._onPinchGestureEvent = Animated.event(
[{ nativeEvent: { scale: this._pinchScale } }],
{ useNativeDriver: USE_NATIVE_DRIVER }
);
/* Rotation */
this._rotate = new Animated.Value(0);
this._rotateStr = this._rotate.interpolate({
inputRange: [-100, 100],
outputRange: ['-100rad', '100rad'],
});
this._lastRotate = 0;
this._onRotateGestureEvent = Animated.event(
[{ nativeEvent: { rotation: this._rotate } }],
{ useNativeDriver: USE_NATIVE_DRIVER }
);
/* Tilt */
this._tilt = new Animated.Value(0);
this._tiltStr = this._tilt.interpolate({
inputRange: [-501, -500, 0, 1],
outputRange: ['1rad', '1rad', '0rad', '0rad'],
});
this._lastTilt = 0;
this._onTiltGestureEvent = Animated.event(
[{ nativeEvent: { translationY: this._tilt } }],
{ useNativeDriver: USE_NATIVE_DRIVER }
);
}
_onRotateHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastRotate += event.nativeEvent.rotation;
this._rotate.setOffset(this._lastRotate);
this._rotate.setValue(0);
}
};
_onPinchHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastScale *= event.nativeEvent.scale;
this._baseScale.setValue(this._lastScale);
this._pinchScale.setValue(1);
}
};
_onTiltGestureStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastTilt += event.nativeEvent.translationY;
this._tilt.setOffset(this._lastTilt);
this._tilt.setValue(0);
}
};
render() {
return (
<View style={styles.background}>
<StatusBar hidden={true}/>
<View style={[styles.bigQuadran]}>
</View>
<Image style={styles.numbers}
source={require("../assets/nobgfreeclock2.png")}/>
<View style={[styles.mediumQuadran]}/>
<PanGestureHandler
ref={this.panRef}
onGestureEvent={this._onTiltGestureEvent}
onHandlerStateChange={this._onTiltGestureStateChange}
minDist={10}
minPointers={2}
maxPointers={2}
avgTouches>
<Animated.View style={[styles.mover
//{transform: [{ translateX: this.translateX, }, { translateY: this.translateY}]}
]}>
<RotationGestureHandler
ref={this.rotationRef}
simultaneousHandlers={this.pinchRef}
onGestureEvent={this._onRotateGestureEvent}
onHandlerStateChange={this._onRotateHandlerStateChange}
>
<Animated.View style={[styles.mover]}>
<PinchGestureHandler
ref={this.pinchRef}
simultaneousHandlers={this.rotationRef}
onGestureEvent={this._onPinchGestureEvent}
onHandlerStateChange={this._onPinchHandlerStateChange}
>
<Animated.View style={[styles.hours,
{transform: [
{ perspective: 200 },
{ scale: this._scale },
{ rotate: this._rotateStr },
{ rotateX: this._tiltStr },
]},
]}/>
</PinchGestureHandler>
</Animated.View>
</RotationGestureHandler>
</Animated.View>
</PanGestureHandler>
{/*
<PanGestureHandler onGestureEvent={this.onPanGestureEvent}>
<Animated.View style={[styles.mover, {
transform: [{ translateX: this.translateX, }, { translateY: this.translateY}]}]}>
<View style={styles.minutes}/>
</Animated.View>
</PanGestureHandler>
<PanGestureHandler onGestureEvent={this.onPanGestureEvent}>
<Animated.View style={[styles.mover, {
transform: [{ translateX: this.translateX, }, { translateY: this.translateY}]}]}>
<View style={styles.seconds}/>
</Animated.View>
</PanGestureHandler>
*/}
<View style={[styles.smallQuadran]}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'whitesmoke',
alignItems: 'center',
justifyContent: 'center',
},
background: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent'
},
mover: {
position: 'absolute',
width: SIZE,
height: SIZE,
borderRadius: SIZE/2,
alignItems: 'center',
justifyContent: 'flex-start',
top: 68
},
hours: {
backgroundColor: 'black',
height: '20%',
marginTop: '30%',
width: 4.5,
borderRadius: 4.5
},
minutes: {
backgroundColor: 'black',
height: '35%',
marginTop: '15%',
width: 3.5,
borderRadius: 3.5
},
seconds: {
backgroundColor: 'rgba(227, 71, 134, 1)',
height: '35%',
marginTop: '15%',
width: 2,
borderRadius: 2
},
bigQuadran: {
width: SIZE * 0.815,
height: SIZE * 0.815,
borderRadius: SIZE * 0.40,
backgroundColor: 'ghostwhite',
borderColor: 'black',
borderWidth: SIZE * 0.030,
position: 'absolute',
top: 100
},
smallQuadran: {
width: 10,
height: 10,
borderRadius: 5,
position: 'absolute',
backgroundColor: 'rgba(227, 71, 134, 1)',
top: 250
},
numbers: {
width: SIZE * 0.78,
height: SIZE * 0.78,
position: 'absolute',
top: 107,
borderRadius: SIZE * 4
}
})

Custom Textinput shows border around itself in IOS - React Native

I've created a custom text input component for my react native app. It has no issues in the android build. But In IOS, the custom component draws a border around its view.
This is how it looks in ios. You can see a border around it.
I want it took to look like this in IOS without a square border
This is my custom component (MyInput.js)
import React, {useState, useRef, useEffect} from 'react';
import {
Animated,
Text,
ActivityIndicator,
TouchableOpacity,
} from 'react-native';
const {View, TextInput, StyleSheet} = require('react-native');
const MyInput = (props) => {
const [hide, makeHide] = useState(false);
const [phAanimatedValue] = useState(new Animated.Value(0));
const [labelAanimatedValue] = useState(new Animated.Value(0));
const [overflow, setOverflow] = useState('hidden');
const movePlaceHolderLeft = phAanimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, -10],
});
const moveLabelRight = labelAanimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [-10, 0],
});
const animatePlaceHolder = (v, hide) => {
Animated.timing(phAanimatedValue, {
toValue: v,
duration: 50,
useNativeDriver: true,
}).start(() => {
makeHide(hide);
animateLabel(v);
});
};
const animateLabel = (v) => {
setOverflow('visible');
Animated.timing(labelAanimatedValue, {
toValue: v,
duration: 50,
useNativeDriver: true,
}).start();
};
return (
<View
style={[
styles.input,
{
overflow: overflow,
borderColor:props.error?"#ff0033":"#e5deda",
},
]}>
<TextInput
onFocus={() => {
animatePlaceHolder(1, true);
}}
ref={props.cref}
value={props.value}
onChangeText={(v) => props.onChangeText(v)}
style={{paddingStart: 25}}
onBlur={() => {
if (props.value.length > 0) {
makeHide(true);
} else {
animatePlaceHolder(0, false);
}
}}
/>
{!hide ? (
<Animated.Text
onPress={() => props.cref.current.focus()}
style={{
position: 'absolute',
transform: [{translateX: movePlaceHolderLeft}],
height: '100%',
fontFamily: 'Muli-Regular',
width: '100%',
marginStart: 20,
paddingVertical:15,
color: props.error ? '#ff0033' : '#a6a6a6',
textAlignVertical: 'center',
}}>
{props.label}
</Animated.Text>
) : (
<Animated.Text
onPress={() => props.cref.current.focus()}
style={{
position: 'absolute',
transform: [{translateX: moveLabelRight}],
marginTop: -10,
zIndex:1000,
backgroundColor: 'white',
color:props.error?'red':'#e5deda',
marginStart: 20,
fontFamily: 'Muli-Regular',
paddingHorizontal: 5,
textAlignVertical: 'top',
}}>
{props.label}
</Animated.Text>
)}
</View>
);
};
const styles = StyleSheet.create({
input: {
borderWidth: 2,
borderColor: '#e5deda',
backgroundColor: 'white',
fontSize: 14,
color: '#353839',
paddingVertical:15,
marginHorizontal: 20,
lineHeight: 18,
fontFamily: 'Muli-Regular',
fontWeight: '600',
borderRadius: 100,
},
});
export default MyInput;
This is How I Render:
<MyInput
value={value}
onChangeText={(v)=>
{
setValue(v)
setError(true)
}}
cref={postalRef}
error={error}
label="Postal Code"
/>
Is there any solution for this or is this a bug?

Updating content on a Screen in React Native makes Animated Components Freeze

The home screen of my react native app has components which animate on scroll. However, any little change to the screen resets the components to their default state and they don't animate anymore.
I am attempting to fetch data and display it on the home screen. Whenever new content is loaded, the animated components go back to their original state.
home/index.tsx
import React, { useEffect } from 'react'
import { View, StyleSheet, StatusBar, Image } from 'react-native'
import Animated, { Extrapolate } from 'react-native-reanimated'
import { useDispatch, useSelector } from 'react-redux'
import { bannerImage, logoImage } from '../../assets/images'
import CategoryContainer from './CategoryContainer'
import colors from '../../styles/colors'
import CstmBigDisplayButton from '../../components/CstmBigDisplayButton'
import globalStyles from '../../styles/globals'
import MenuButton from '../../components/MenuButton'
import TranslucentStatusBar from '../../components/TranslucentStatusBar'
import { getCategories } from '../../redux/actions/categoryAction'
import { RootState } from '../../redux/types'
const HEADER_MAX_HEIGHT = 430
const HEADER_MIN_HEIGHT = 100
const Home = ({ navigation }: any) => {
const { categories } = useSelector((state: RootState) => state.categories)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getCategories())
}, [])
let scrollY = new Animated.Value(0)
const headerHeight = Animated.interpolate(
scrollY,
{
inputRange: [0, HEADER_MAX_HEIGHT],
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT],
extrapolate: Extrapolate.CLAMP,
}
)
const animateOverlay = Animated.interpolate(
scrollY,
{
inputRange: [0, HEADER_MAX_HEIGHT / 2, HEADER_MAX_HEIGHT - 30],
outputRange: [1, 0.5, 0.1],
extrapolate: Extrapolate.CLAMP,
}
)
const menuOpacity = Animated.interpolate(
scrollY,
{
inputRange: [0, HEADER_MAX_HEIGHT - 30],
outputRange: [0.5, 1],
extrapolate: Extrapolate.CLAMP
}
)
return (
<>
<TranslucentStatusBar />
<Animated.View
style={[
styles.header,
{
height: headerHeight,
elevation: 10
}
]}
>
<View style={styles.headerBackground} >
<Animated.View
style={{
position: 'absolute',
top: 0,
right: 0,
zIndex: 11,
marginTop: StatusBar.currentHeight,
opacity: menuOpacity,
}}
>
<View style={{ margin: 16 }}>
<MenuButton onPress={() => {navigation.openDrawer()}}/>
</View>
</Animated.View>
<Animated.Image
source={bannerImage}
resizeMode='cover'
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
height: headerHeight,
opacity: animateOverlay,
}}/>
<Animated.View
style={[
styles.overlay,
{
backgroundColor: animateOverlay
}
]}
>
<Image
source={logoImage}
style={styles.logo}
resizeMode='contain'
/>
</Animated.View>
</View>
</Animated.View>
<Animated.ScrollView
scrollEventThrottle={16}
style={globalStyles.screenDefaults}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{
useNativeDriver: true,
listener: (event: any) => console.log(event)
}
)}
>
<View style={styles.contentContainer}>
<CategoryContainer
titleStyle={[styles.general]}
titleText='General Categories'
titleTextStyle={{ color: colors.primary["500TextColor"] }}
categories={categories.filter((category: any) => !category.special)}
navigation={navigation}
/>
<View style={styles.divider}></View>
<CategoryContainer
titleStyle={[styles.special]}
titleText='Special Categories'
titleTextStyle={{ color: colors.secondary["700TextColor"] }}
categories={categories.filter((category: any) => category.special)}
navigation={navigation}
/>
</View>
<CstmBigDisplayButton />
</Animated.ScrollView>
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1
},
header: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
height: HEADER_MAX_HEIGHT,
backgroundColor: 'grey',
zIndex: 10,
alignContent: 'center',
justifyContent: 'center'
},
headerBackground: {
width: '100%',
flex: 1,
backgroundColor: '#FFF'
},
logo: {
flex: 1,
height: undefined,
width: undefined,
},
overlay: {
flex: 1,
paddingTop: StatusBar.currentHeight
},
contentContainer: {
flexDirection: 'row',
flex: 1,
paddingTop: HEADER_MAX_HEIGHT,
paddingBottom: 24
},
general: {
backgroundColor: colors.primary[500],
color: colors.primary["500TextColor"]
},
special: {
backgroundColor: colors.secondary[700],
color: colors.secondary["700TextColor"]
},
divider: {
backgroundColor: '#909090',
height: '99%',
width: '0.1%'
},
})
export default Home
globals.ts
import { StyleSheet, StatusBar } from 'react-native'
import theme, { standardInterval } from './theme'
const globalStyles = StyleSheet.create({
gutterBottom: {
marginBottom: 8
},
captionText: {
fontSize: 12
},
screenDefaults: {
flex: 1,
backgroundColor: theme.pageBackgroundColor,
},
headlessScreenDefaults: {
paddingTop: StatusBar.currentHeight,
padding: 16
},
iconText: {
flexDirection: 'row',
alignItems: 'center'
},
iconTextMargin: {
marginLeft: 4
},
label: {
fontSize: 16,
marginBottom: 4,
paddingLeft: 12,
color: '#555'
},
textInput: {
padding: 0,
fontSize: 16,
color: '#444',
marginLeft: 6,
marginRight: 8,
flex: 1
},
textInputContainer: {
borderWidth: 1,
borderColor: '#ddd',
backgroundColor: 'white',
borderRadius: standardInterval(0.5),
padding: 8,
flexDirection: 'row',
alignItems: 'center'
},
helperText: {
marginTop: 4,
paddingLeft: 12,
fontSize: 12,
color: '#555'
},
button: {
padding: standardInterval(1.5),
borderRadius: standardInterval(.5),
// marginLeft: 12,
},
})
export default globalStyles
Points worth noting.
When the content is fetched from the drawer navigation component, the animated components work just fine. This is not reliable since if the data is received when the home screen is already mounted, the components will not animate.
DrawerNavigation.tsx
import React, { useEffect } from 'react'
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem, DrawerContentComponentProps, } from '#react-navigation/drawer'
import { NavigationContainer } from '#react-navigation/native'
import { useSelector, useDispatch } from 'react-redux'
import AuthNavigator from './AuthNavigator'
import MainNavigator from './MainNavigator'
import theme from '../styles/theme'
import colors from '../styles/colors'
import Profile from '../screens/profile'
import { RootState } from '../redux/types'
import { verifyToken } from '../redux/actions/authAction'
import Splash from '../screens/splash'
import { logOut } from '../redux/actions/authAction'
import { getMetadata } from '../redux/actions/metadataActions'
import { getCategories } from '../redux/actions/categoryAction'
const Drawer = createDrawerNavigator()
const DrawerNavigator = () => {
const dispatch = useDispatch()
const { hasInitiallyLoaded, authenticationToken, authenticatedUser } = useSelector((state: RootState) => state.auth)
useEffect(() => {
if (!hasInitiallyLoaded) dispatch(verifyToken(authenticationToken))
})
useEffect(() => {
dispatch(getMetadata())
dispatch(getCategories())
}, [getMetadata])
return (
<>
{
hasInitiallyLoaded
? <NavigationContainer>
<Drawer.Navigator
drawerStyle={{ backgroundColor: theme.pageBackgroundColor }}
drawerContentOptions={{ activeTintColor: colors.secondary[500] }}
drawerContent={(props: DrawerContentComponentProps) => (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
{
authenticatedUser
&& <DrawerItem
label='log out'
onPress={() => {dispatch(logOut([() => props.navigation.navigate('home')]))}}
/>
}
</DrawerContentScrollView>
)}
>
<Drawer.Screen name='home' component={MainNavigator} />
{
authenticatedUser
? <Drawer.Screen name='profile' component={Profile} />
: <Drawer.Screen name='login' component={AuthNavigator} />
}
</Drawer.Navigator>
</NavigationContainer>
: <Splash />
}
</>
)
}
export default DrawerNavigator
Also, the listener in Animated.event() in the Animated.ScrollView does not work
Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{
useNativeDriver: true,
listener: (event: any) => console.log(event)
}
)}
After placing the scrollY view in state, everything seemed to work as expected.

React native state is not updating using hooks in animated.start()

import React, { useRef, useState, useEffect } from "react";
import {
Animated,
Dimensions,
View,
StyleSheet,
PanResponder,
Text,
Image,
} from "react-native";
import { catarray } from "./categoryimages";
const { width, height } = Dimensions.get("screen");
const App = () => {
const pan = useRef(new Animated.ValueXY()).current;
const [currentIndex, setCurrentIndex] = useState(0);
const rotate = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: ["-10deg", "0deg", "10deg"],
extrapolate: "clamp",
});
useEffect(() => {
pan.setValue({ x: 0, y: 0 });
}, [currentIndex]);
const nextCardOpacity = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [1, 0, 1],
extrapolate: "clamp",
});
const nextCardScale = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [1, 0.8, 1],
extrapolate: "clamp",
});
const renderImages = () => {
let rotateandtranslate = {
transform: [{ rotate: rotate }, ...pan.getTranslateTransform()],
};
return catarray
.map((item, index) => {
if (index < currentIndex) {
return null;
} else if (index === currentIndex) {
return (
<Animated.View
key={index}
style={[
rotateandtranslate,
{
width: width * 0.9,
height: height * 0.85,
position: "absolute",
},
]}
{...panResponder.panHandlers}
>
<Image
source={{ uri: item.uri }}
style={{
flex: 1,
height: null,
width: null,
borderRadius: 30,
}}
/>
</Animated.View>
);
} else {
return (
<Animated.View
key={index}
style={[
{
opacity: nextCardOpacity,
transform: [{ scale: nextCardScale }],
},
{
width: width * 0.9,
height: height * 0.85,
position: "absolute",
},
]}
>
<Image
source={{ uri: item.uri }}
style={{
flex: 1,
height: null,
width: null,
borderRadius: 30,
}}
/>
</Animated.View>
);
}
})
.reverse();
};
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
pan.setOffset({
x: pan.x._value,
y: pan.y._value,
});
},
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }]),
onPanResponderRelease: (e, gestureState) => {
if (gestureState.dx > 140) {
Animated.spring(pan, {
toValue: { x: width + width, y: gestureState.dy },
}).start(() => {
setCurrentIndex(currentIndex + 1);
});
} else if (gestureState.dx < -140) {
Animated.spring(pan, {
toValue: { x: -width - width, y: gestureState.dy },
}).start(() => {
setCurrentIndex(currentIndex + 1);
});
} else {
Animated.spring(pan, {
toValue: { x: 0, y: 0 },
friction: 2,
}).start();
}
},
})
).current;
return <View style={styles.container}>{renderImages()}</View>;
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
titleText: {
fontSize: 14,
lineHeight: 24,
fontWeight: "bold",
},
box: {
height: 150,
width: 150,
backgroundColor: "blue",
borderRadius: 5,
},
});
export default App;
I have been trying a tinder swipe animation. in react native hooks and everything works fine for the first two cards.But after that the currentIndex remains at "1" and is not updating.But i found out that the currentIndex value is reinitializing to 0 somehow.The value of currentIndex doesn't update even though im calling setCurrentIndex(currentIndex+1).Please help me if anyone knows where the problem is.Thank you.

Nested PinchGestureHandler & PanGestureHandler not working in Android

I am trying to build a full screen image view with zoom and pan capabilities in react native. I am using react-native-gesture-handler for handling multi touches. It is working fine on iOS but does nothing on Android.
I am completely confused on why this code would work on iOS but not on Android. I can confirm that my react-native-gesture-handler setup is working because I have another PanGestureHandler working as expected.
/*********************** Imports ***********************/
import React, { useRef, useEffect } from 'react';
import {
View, StyleSheet, Animated,
} from 'react-native';
import ZoomImage from './ZoomableImage';
import { vw, vh, isiPhoneX } from '../Styles/StyleUtils';
/********************* End Imports *********************/
/*********************** ImageView Function ***********************/
const ImageView = ({ dismiss, uri, imageLayout }) => {
const animation = useRef(new Animated.Value(0));
useEffect(() => {
Animated.timing(animation.current, {
toValue: 1,
duration: 250,
}).start();
}, []);
function closeImage() {
Animated.timing(animation.current, {
toValue: 0,
duration: 250,
}).start(() => dismiss());
}
return (
<View style={styles.mainContainer}>
<Animated.View style={[
styles.container,
{
backgroundColor: animation.current.interpolate({
inputRange: [0, 1],
outputRange: ["rgba(0, 0, 0, 0)", "rgba(0, 0, 0, 0.5)"],
})
}
]}>
{/* <View style={styles.header}>
<TouchableOpacity style={styles.closeBtn} onPress={closeImage}>
<Icon name="close" color={blackColor} size={30} />
</TouchableOpacity>
</View> */}
<ZoomImage dismiss={closeImage} imageStyle={[
{
left: animation.current.interpolate({
inputRange: [0, 1],
outputRange: [imageLayout.pageX, 0] // 0 : 150, 0.5 : 75, 1 : 0
}),
top: animation.current.interpolate({
inputRange: [0, 1],
outputRange: [imageLayout.pageY, vh(24)] // 0 : 150, 0.5 : 75, 1 : 0
}),
width: animation.current.interpolate({
inputRange: [0, 1],
outputRange: [imageLayout.width, vw(100)],
}),
height: animation.current.interpolate({
inputRange: [0, 1],
outputRange: [imageLayout.height, vw(100)],
})
}
]}
source={{ uri: uri }} />
</Animated.View>
</View>
);
};
/********************** End ImageView Function *******************/
export default ImageView;
const styles = StyleSheet.create({
header: {
marginTop: isiPhoneX() ? 40 : 25,
alignItems: "flex-end"
},
closeBtn: {
paddingHorizontal: 20,
},
mainContainer: {
position: "absolute",
width: vw(100),
height: vh(100)
},
container: {
position: "absolute",
top: 0,
left: 0,
right: 0, bottom: 0,
},
image: {
position: "absolute",
}
});
/*********************** Imports ***********************/
import React, { useEffect, useState, useCallback, useRef } from 'react';
import { Animated } from 'react-native';
import {
State,
PanGestureHandler,
PinchGestureHandler
} from 'react-native-gesture-handler';
import { vw, vh } from '../Styles/StyleUtils';
/********************* End Imports *********************/
/*********************** ZoomableImage Function ***********************/
const ZoomableImage = (props) => {
const panRef = useRef();
const pinchRef = useRef();
const closeAnimation = useRef(new Animated.ValueXY({ x: 0, y: 0 }));
const scaleAnimation = useRef(new Animated.Value(1));
const baseScale = useRef(new Animated.Value(1));
const scale = useRef(Animated.multiply(baseScale.current, scaleAnimation.current));
const [lastScale, setLastScale] = useState(1);
useEffect(() => {
console.log('Refs', panRef);
}, [panRef.current]);
const onPanHandlerGesture = useCallback(({ nativeEvent }) => {
console.log('Native Event', nativeEvent);
closeAnimation.current.setValue({
x: nativeEvent.translationX,
y: nativeEvent.translationY
});
}, []);
const onPanHandlerStateChange = useCallback(({ nativeEvent }) => {
console.log('New Pan Event', nativeEvent);
if (nativeEvent.oldState === State.ACTIVE) {
if (
nativeEvent.translationY > 250
|| nativeEvent.velocityY > 1200
) {
Animated.parallel([
Animated.timing(scaleAnimation.current, {
toValue: 1,
duration: 200
}),
Animated.timing(baseScale.current, {
toValue: 1,
duration: 200
}),
Animated.timing(closeAnimation.current, {
toValue: { x: 0, y: 0 },
duration: 200
})
]).start(() => props.dismiss());
}
else {
Animated.timing(closeAnimation.current, {
toValue: { x: 0, y: 0 },
duration: 100
}).start();
}
}
}, [lastScale]);
const onPinchGestureEvent = Animated.event([{ nativeEvent: { scale: scaleAnimation.current } }]);
useCallback(({ nativeEvent }) => {
scaleAnimation.current.setValue(nativeEvent.scale);
}, [lastScale]);
const onPinchHandlerStateChange = ({ nativeEvent }) => {
console.log('New Pinch Event', nativeEvent);
if (nativeEvent.oldState === State.ACTIVE) {
const newLastScale = lastScale * nativeEvent.scale;
setLastScale(newLastScale);
baseScale.current.setValue(newLastScale);
scaleAnimation.current.setValue(1);
}
};
return (
<PanGestureHandler maxPointers={2} avgTouches onHandlerStateChange={onPanHandlerStateChange}
minDist={10} onGestureEvent={onPanHandlerGesture} ref={panRef}>
<PinchGestureHandler ref={pinchRef} simultaneousHandlers={panRef}
onHandlerStateChange={onPinchHandlerStateChange} onGestureEvent={onPinchGestureEvent}>
<Animated.Image style={[
props.imageStyle,
{
transform: [
{ perspective: 1000 },
{
translateY: closeAnimation.current.y.interpolate({
inputRange: [-vh(25), vh(25)],
outputRange: [-vh(25), vh(25)],
extrapolate: "clamp"
})
},
{
translateX: closeAnimation.current.x.interpolate({
inputRange: [-vw(25), vw(25)],
outputRange: [-vw(10), vw(10)],
extrapolate: "clamp"
})
},
{
scale: scale.current.interpolate({
inputRange: [1, 2.5],
outputRange: [1, 2.5],
extrapolate: "clamp"
})
}
]
}
]} source={props.source} />
</PinchGestureHandler>
</PanGestureHandler>
);
};
ZoomableImage.defaultProps = {
imageStyle: {},
source: { uri: "" }
};
/********************** End ZoomableImage Function *******************/
export default ZoomableImage;
Could someone please help me?
according to me you need to include an other Animated.View (only acting as a wrapper) between your PanGH and PinchGH componant.
return (
<PanGestureHandler ...>
<Animated.View...>
<PinchGestureHandler ...>
<Animated.Image...>

Categories