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...>
Related
Specifically, how do I make one side of the card disappear once flipped. I am using Android, so backfaceVisibility did not help solve my problem. I used animate to remove the object via opacity, but the problem is that once I removed one side of the card, the 'invisible' buttons still worked, and the buttons on the current side of the card did not. I tried playing around with zIndex and 'disabled' in Pressable, and I am unsure how to fix this. I have attached source code below:
let animatedValue = new Animated.Value(0)
let val = 0;
animatedValue.addListener(({ value }) => {
val = value;
})
let frontOpacity = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [1, 0]
})
let backOpacity = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [0, 1]
})
let frontInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg']
})
let backInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})
const frontAnimatedStyle = {
transform: [
{ rotateY: frontInterpolate }
],
opacity: frontOpacity,
}
const backAnimatedStyle = {
transform: [
{ rotateY: backInterpolate }
],
opacity: backOpacity,
}
let isFront = true;
const flipCard = () => {
isFront = !isFront;
if (val >= 90) {
Animated.spring(animatedValue, {
toValue:0,
friction: 8,
tension: 10,
useNativeDriver: true,
}).start();
} else {
Animated.spring(animatedValue, {
toValue:180,
friction: 8,
tension: 10,
useNativeDriver: true,
}).start();
}
}
const getFrontZ = () => {
return isFront ? 1 : 0;
}
return(
<View style={styles.container}>
<Animated.View
style={[styles.cardStyle,
styles.frontCardStyle,
frontAnimatedStyle,
{height: cardHeight, zIndex: -1,}]}>
<Pressable
onPress={() => console.log('Open Stack')}
style={{height: '100%', width: '100%'}}>
<Pressable
onPress={() => flipCard()}
style={{backgroundColor: 'blue', height: 50, width: 50,}}
>
</Pressable>
</Pressable>
</Animated.View>
<Animated.View
style={[styles.cardStyle,
styles.backCardStyle,
backAnimatedStyle,
{height: cardHeight, zIndex: 0}]}>
<Pressable
onPress={() => flipCard()}
style={{height: '100%', width: '100%'}}
>
</Pressable>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
cardStyle: {
height: '100%',
width: '100%',
backfaceVisibility: 'hidden',
},
frontCardStyle: {
backgroundColor: 'red',
},
backCardStyle: {
backgroundColor: 'purple',
position: 'absolute',
top: 0,
}
})
try this library react-native-card-flip
and import this in your file and use like this
import CardFlip from 'react-native-card-flip';
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} >
<Text>AB</Text></TouchableOpacity>
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} >
<Text>CD</Text></TouchableOpacity>
</CardFlip>
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.
I have created a custom tab bar component using react native navigation following this tutorial: https://reactnavigation.org/docs/bottom-tab-navigator#tabbar. I have then placed a custom component on top of the navigation to mimick spotify minimized player so that when I can execute a drag gesture from minimized to a full sized player. However, When I run the app, the gesture works fine on the first screen, but when I switch screens the gesture doesn't work https://www.youtube.com/watch?v=w-8NSQyWPHI&feature=youtu.be.
import React, { Fragment, useState } from 'react';
import { Text, View, TouchableOpacity, Dimensions, StyleSheet } from 'react-native'
import { PanGestureHandler, State } from 'react-native-gesture-handler'
import { clamp, onGestureEvent, timing, withSpring } from 'react-native-redash'
import Animated from 'react-native-reanimated'
import { getBottomSpace } from 'react-native-iphone-x-helper'
import Player from './player/Player'
import MiniPlayer from './player/MiniPlayer'
const { height } = Dimensions.get('window')
const TABBAR_HEIGHT = getBottomSpace() + 50
const MINIMIZED_PLAYER_HEIGHT = 52
const SNAP_TOP = 0
const SNAP_BOTTOM = height - TABBAR_HEIGHT - MINIMIZED_PLAYER_HEIGHT
const config = {
damping: 30,
mass: 1,
stiffness: 150,
overshootClamping: false,
restSpeedThreshold: 0.1,
restDisplacementThreshold: 0.1
}
const {
Clock,
Value,
cond,
useCode,
set,
block,
not,
clockRunning,
interpolate,
diffClamp,
Extrapolate
} = Animated
const styles = StyleSheet.create({
container: {
flex: 1,
},
playerSheet: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'white'
}
})
export default ({ statee, descriptors, navigation }) => {
const translationY = new Value(0)
const velocityY = new Value(0)
const state = new Value(State.UNDETERMINED)
const offset = new Value(SNAP_BOTTOM)
const goUp: Animated.Value<0 | 1> = new Value(0)
const goDown: Animated.Value<0 | 1> = new Value(0)
const gestureHandler = onGestureEvent({
state,
translationY,
velocityY
})
const translateY = clamp(
withSpring({
state,
offset,
value: translationY,
velocity: velocityY,
snapPoints: [SNAP_TOP, SNAP_BOTTOM],
config
}),
SNAP_TOP,
SNAP_BOTTOM
)
const translateBottomTab = interpolate(translateY, {
inputRange: [SNAP_TOP, SNAP_BOTTOM],
outputRange: [TABBAR_HEIGHT, 0],
extrapolate: Extrapolate.CLAMP
})
const opacity = interpolate(translateY, {
inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const opacity2 = interpolate(translateY, {
inputRange: [
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const clock = new Clock()
useCode(
block([
cond(goUp, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_TOP
})
),
cond(not(clockRunning(clock)), [set(goUp, 0)])
]),
cond(goDown, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_BOTTOM
})
),
cond(not(clockRunning(clock)), [set(goDown, 0)])
])
]),
[]
)
goUpHandler = () => {
console.log('TAPPERD UP');
goUp.setValue(1)
}
goDownHandler = () => {
console.log('TAPPERD DOWN');
goDown.setValue(1)
}
return (
<>
<PanGestureHandler {...gestureHandler}>
<Animated.View
style={[styles.playerSheet, { transform: [{ translateY }] }]}
>
<Player onPress={this.goDownHandler} />
<Animated.View
pointerEvents='none'
style={{
opacity: opacity2,
backgroundColor: 'white',
...StyleSheet.absoluteFillObject
}}
/>
<Animated.View style={{
opacity: opacity,
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: MINIMIZED_PLAYER_HEIGHT
}}>
<MiniPlayer onPress={this.goUpHandler} />
</Animated.View>
</Animated.View>
</PanGestureHandler>
<Animated.View style={{
flexDirection: 'row',
height: TABBAR_HEIGHT,
paddingTop: 8,
justifyContent: 'center',
backgroundColor: 'white',
transform: [{ translateY: translateBottomTab }]
}}>
{statee.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const Icon = options.tabBarIcon
const isFocused = statee.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<Animated.View style={styles.container} key={index}>
<Animated.View style={{
alignItems: 'center',
justifyContent: 'center'
}}>
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
>
<Icon />
</TouchableOpacity>
</Animated.View>
</Animated.View>
);
})}
</Animated.View>
</>
);
}
I have the exact issue. This is because the reanimated is messed up when re-rendering.
I fixed that by moving the below code out of the functional/class component to make it global.
const translationY = new Value(0)
const velocityY = new Value(0)
const state = new Value(State.UNDETERMINED)
const offset = new Value(SNAP_BOTTOM)
const goUp: Animated.Value<0 | 1> = new Value(0)
const goDown: Animated.Value<0 | 1> = new Value(0)
const gestureHandler = onGestureEvent({
state,
translationY,
velocityY
})
const translateY = clamp(
withSpring({
state,
offset,
value: translationY,
velocity: velocityY,
snapPoints: [SNAP_TOP, SNAP_BOTTOM],
config
}),
SNAP_TOP,
SNAP_BOTTOM
)
const translateBottomTab = interpolate(translateY, {
inputRange: [SNAP_TOP, SNAP_BOTTOM],
outputRange: [TABBAR_HEIGHT, 0],
extrapolate: Extrapolate.CLAMP
})
const opacity = interpolate(translateY, {
inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const opacity2 = interpolate(translateY, {
inputRange: [
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
],
outputRange: [0, 1],
extrapolate: Extrapolate.CLAMP
})
const clock = new Clock()
useCode(
block([
cond(goUp, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_TOP
})
),
cond(not(clockRunning(clock)), [set(goUp, 0)])
]),
cond(goDown, [
set(
offset,
timing({
clock,
from: offset,
to: SNAP_BOTTOM
})
),
cond(not(clockRunning(clock)), [set(goDown, 0)])
])
]),
[]
)
I've been messing around with React Native and I've tried to create a simple card that flips around on press (back and forth):
I had it working as a class component but now I've tried to refactor to a functional component and its only flipping one way but not back on second press.
Can someone advise me what I've missed? :/
I tried using useState and useEffect for the value and animated value etc, but no dice...
import React from 'react';
import { View, StyleSheet, Animated, TouchableWithoutFeedback, Text } from 'react-native';
import TabBarIcon from '../components/TabBarIcon';
const App = () => {
let animatedValue = new Animated.Value(0);
let value = 0;
animatedValue.addListener(({ value }) => {
this.value = value;
})
let frontInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
})
let backInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})
let frontOpacity = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [1, 0]
});
let backOpacity = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [0, 1]
});
let elevationFront = animatedValue.interpolate({
inputRange: [0, 25],
outputRange: [10, 0]
})
let elevationBack = animatedValue.interpolate({
inputRange: [155, 180],
outputRange: [0, 10]
})
const flipCard = () => {
if (value >= 90) {
Animated.spring(animatedValue,{
toValue: 0,
friction: 8,
tension: 10
}).start();
} else {
Animated.spring(animatedValue,{
toValue: 180,
friction: 8,
tension: 10
}).start();
}
}
const frontAnimatedStyle = {
transform: [{ rotateY: frontInterpolate}]
}
const backAnimatedStyle = {
transform: [{ rotateY: backInterpolate }]
}
return (
<TouchableWithoutFeedback onPress={() => flipCard()} >
<View>
<Animated.View style={[frontAnimatedStyle, styles.paperFront,{elevation: elevationFront}, {opacity: frontOpacity}]}>
<Text style={{fontSize: 20,paddingTop: 8, paddingLeft: 8, color: 'black',lineHeight: 20}}>
Title Front {value} - <Text style={{fontSize: 8}}>KPI</Text>
</Text>
<View style={{position: "absolute", paddingTop: 3, right: 8}}>
<TabBarIcon style={{color: "black"}} name={"md-more"} />
</View>
</Animated.View>
<Animated.View style={[backAnimatedStyle, styles.paperBack, {elevation: elevationBack}, {opacity: backOpacity}]}>
<Text>Back title {value}</Text>
</Animated.View>
</View>
</TouchableWithoutFeedback>
);
}
const styles = StyleSheet.create({
paperFront : {
marginHorizontal: 15,
backgroundColor: "white",
minHeight: 200,
borderRadius: 5,
marginBottom: 15,
},
paperBack : {
top: -215,
marginHorizontal: 15,
backgroundColor: "white",
minHeight: 200,
borderRadius: 5,
marginBottom: 15,
}
});
export default App
You need to call your "value" not "this.value", but since you already got "value" parameter I suggest you change the variable name:
let val = 0;
animatedValue.addListener(({ value}) => {
val = value;
});
P.S: don't forget to change your calls to "value" variable into "val"
fix it
if (this.value >= 90) {
in expo
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;