React-native: prevent Swipeable component swipe to outside the screen - react-native

I have a screen that show chat messages, and I make the item chat able to swipe to reply like FB Messenger. I'm using Swipeable component from react-native-gesture-handler. But I have a troble: whenever I swipe fastly, the item will swipe to outside the screen like this: https://i.stack.imgur.com/uIEWN.png
How can I fix it?
My code for Swipeable Item:
<Swipeable
failOffsetX={
!isMyMessage ? [0, SCREEN_WIDTH / 3] : [-SCREEN_WIDTH / 2, 0]
}
ref={swipeableRef}
friction={2}
enableTrackpadTwoFingerGesture
renderLeftActions={renderSwipeableActions }
renderRightActions={renderSwipeableActions }
onSwipeableClose={onSwipeableClose}
onSwipeableOpen={() => {}} />
And this one for render UI when swipe action activate :
const renderSwipeableActions = (
_progress: Animated.AnimatedInterpolation,
dragX: Animated.AnimatedInterpolation,
) => {
const transformLeft = dragX.interpolate({
inputRange: [0, 50, 100, 101],
outputRange: [-20, 0, 0, 1],
extrapolate: 'clamp',
});
const transformRight = dragX.interpolate({
inputRange: [0, 50, 100, 101],
outputRange: [1, 0, 0, -20],
extrapolate: 'clamp',
});
return (
<RectButton
style={{
flex: 1,
alignItems: !isMyMessage ? 'flex-start' : 'flex-end',
}}>
<Animated.View
style={[
{
transform: [
{translateX: !isMyMessage ? transformLeft : transformRight},
],
},
]}>

Related

React Native interpolate background color not working with rgba alpha transparency

I want to interpolate the backgroundColor of a View.
This is my animation:
const [myAnimation] = useState(new Animated.Value(0));
const setInteraction = (focused) => {
if (focused) {
Animated.timing(myAnimation, {
toValue: 1,
duration: 160,
useNativeDriver: false,
}).start();
}
if (!focused) {
Animated.timing(myAnimation, {
toValue: 0,
duration: 160,
useNativeDriver: false,
}).start();
}
return;
};
And here is how I use it to interpolate the backgroundColor:
<Animated.View
style={{
backgroundColor: myAnimation.interpolate({
inputRange: [0, 1],
outputRange: [
'rgba(27,43,89,0.06)',
'rgba(255,255,255,1)',
],
}),
}}
>
{...}
</Animated.View>
The problem is that since the initial color has a 6% opacity it doesn't work 100% as I expected. It goes from the initial color to an intermediate one and finally ends up in the target color (white):
https://recordit.co/czrVd5Q38I
If the color was 100% opaque it will work, for example:
<Animated.View
style={{
backgroundColor: myAnimation.interpolate({
inputRange: [0, 1],
outputRange: [
'rgba(27,43,89,1)',
'rgba(255,255,255,1)',
],
}),
}}
>
{...}
</Animated.View>
https://recordit.co/HSvJNV4ldg
Any ideas? I've tried playing with the inputRange property but still doesn't work.
UPDATE:
I've realised that the problem is the white color as a target. If I try playing with the alpha value of the same initial / target color it works:
outputRange: [
'rgba(27,43,89,0.06)',
'rgba(27,43,89,0.1)',
],

How to get image to take up whole screen on press using Transform with React Native

I'm creating a chat app and want to make a picture take up the whole screen when it is clicked on like in other chat apps. The code I have so far scales it to the whole screen and centers it horizontally, but only because I know where on the X axis it is starting at and then calculate the translateX from there. The problem is that, for the Y axis, an image can be anywhere on the screen when it is clicked so I can't do the same thing I did for translateX. How can I get the Y position of the image to calculate how much I need to translate it on the Y Axis.
I know there were similar solutions that use spring, but I like the look of the image expanding from where it begins rather than it coming up from the bottom.
Here is my current code:
<Animated.View
style={[
styles.fadingContainer,
{
transform: [
{
scale: Anim.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1, 1.5, 2],
}),
},
{
translateX: Anim.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [
0,
Dimensions.get("window").width / 16,
Dimensions.get("window").width / 8,
],
}),
},
{
translateY: Anim.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [
** What do I put here? **
],
}),
},
],
},
]}
>
<Text style={styles.fadingText}>Fading View!</Text>
</Animated.View>

TouchableOpacity is not working when Transform Animation is applied

Below code is to render a Touchable button with Transform Animation.
const { scrollY, headerScrollDistance } = this.state;
const profileImageTranslateX = scrollY.interpolate({
inputRange: [0, headerScrollDistance],
outputRange: [0, -(ScreenWidth /2) + 32],
extrapolate: 'clamp',
});
const profileImageTranslateY = scrollY.interpolate({
inputRange: [0, headerScrollDistance],
outputRange: [0, -11],
extrapolate: 'clamp',
});
const profileImageScale = scrollY.interpolate({
inputRange: [0, headerScrollDistance / 2, headerScrollDistance],
outputRange: [1, 0.8, 0.6],
extrapolate: 'clamp',
});
return (
<Animated.View
style={[
Styles.animatedView.profileStyle,
{
transform: [
{ translateX: profileImageTranslateX },
{ translateY: profileImageTranslateY },
{ scale: profileImageScale }
]
}
]}
>
<TouchableOpacity activeOpacity={0.5} onPress={() => this.props.history.push('./profilePhotoChanger')}>
<ImageComp profileImageUrl={profileimageurl} imageStyle={Styles.homePageImageStyle} />
</TouchableOpacity>
</Animated.View>
);
As page scrolls, Animation applies to Touchable button. Button is working as expected when transform animation is not applied. But not working when animation is applied. If page comes back to it's normal state(i.e scrolling back) then button works as expected.
Is it normal behaviour in react-native that TouchableOpacity's onPress wont't work when animation applied? or is something wrong with my code?
You can try one of the following option if it works for you
1- import { TouchableOpacity } from 'react-native-gesture-handler';
2- Change height of Animated.View which contain TouchableOpacity (to
fit size of TouchableOpacity)
3- By moving <Animated.View> inside TouchableOpacity
Look like there is open discussion on Touchableopacity not working inside Animated.View

interpolate cause `node cannot be cast to number`

An re-animation (1.13) is used to toggle accordion in React Native app. When accordion is open or close, an arrow is up or down. interpolate is used to animate when arrow from up to down or vise verse.
import Animated, { useValue, interpolate, Easing, useCode, State, greaterThan, lessThan } from "react-native-reanimated";
const animatedController = useValue(0); //<<==animated value between [0, 1]
const arrowAngle = interpolate(animatedController, { //<<==interpolate causes error of node cannot be cast to number
inputRange: [0, 0.5, 1],
outputRange: ['0rad', `${0.5*Math.PI}rad`,`${Math.PI}rad`],
extrapolate: Extrapolate.CLAMP,
});
return (
<>
<TouchableWithoutFeedback onPress={() => setOpen(!open)}>
<View style={styles.titleContainer}>
<Text>{title}</Text>
<Animated.View style={{ transform: [{ rotateZ: arrowAngle }] }}> //<<==arrowAngle call here
<Icon name="chevron-down-outline" size={20} />
</Animated.View>
</View>
</TouchableWithoutFeedback>
</>
)
However the method above causes the error of node cannot be cast to number as below. This interpolate seems very simple. What's wrong here?
The output range shall be a number like this:
const arrowAngle = interpolate(animatedController, {
inputRange: [0, 0.5, 1],
outputRange: [0.01, 1/2*Math.PI,Math.PI], //<<==shall be digital
extrapolate: Extrapolate.CLAMP,
});

Prevent negative animated value on scroll

I have header collapsed on scroll event. On ios scroll offset value can be negative and it cause problem.
Is there any way to prevent animated value being negative?
Here is my code
const ScrollYDiffClamp = Animated.diffClamp(positionY, 0, 60);
const headerTranslate = ScrollYDiffClamp.interpolate({
inputRange: [0, 1],
outputRange: [0, -1],
});
return (
<>
<Header style={{transform: [{translateY: headerTranslate}]}}/>
<Animated.ScrollView
onScroll={Animated.event([{nativeEvent: {contentOffset: {y: positionY}}}], {
useNativeDriver: true,
})}>
</Animated.ScrollView>
</>
)
I believe you can disable bounces on your Animated.ScrollView
bounces={false}
or you can add additional interpolate and fix this issue
const headerHeight = 50;
const clampedScrollY = scrollY.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
const diffClampScrollY = Animated.diffClamp(clampedScrollY, 0, headerHeight);
const translateY = diffClampScrollY.interpolate({
inputRange: [0, headerHeight],
outputRange: [0, -headerHeight],
extrapolate: 'clamp',
});
Try adding this to your interpolation configuration.
extrapolate: "clamp"
Oleskii's answer worked for me, the only issue I was having was on the bottom the issue that this solved on the top was still happening. Just adding more to the max height seems to have resolved the issue though. Something like:
const diffClampScrollY = Animated.diffClamp(clampedScrollY, 0, headerHeight + 300);
the bouncing that happens in the ScollView seems to be covered with 300 added to the max output of the diffClamp