React Native nested Interpolate Animated - react-native

I try to do something like this but is error.
onPress a Btn the height of the Animated View change from HEIGHT1 (Value=0) to INTERPOLATE_1 (Value=1). And after this change I want when scrolling the ScrollView the height Value change to HEIGHT3.
const SCROLLING = useRef(new Animated.Value(0)).current;
const INTERPOLATE_1 = SCROLLING.interpolate({
inputRange: [0, 100],
outputRange:[ HEIGHT2 , HEIGHT3 ],
})
const HEIGHT_ANIM = useRef(new Animated.Value(0)).current
const INTERPOLATE_2 = HEIGHT_ANIM.interpolate({
inputRange: [0,1],
outputRange: [ HEIGHT1, INTERPOLATE_1 ],
})
<Animated.View style={{
height: INTERPOLATE_2
>
<Animated.ScrollView
scrollEventThrottle={16}
style={{flex:1}}
onScroll={Animated.event(
[{
nativeEvent: {
contentOffset: {
//
y: SCROLLING,
},
},
}],
{ useNativeDriver: false },
)}
</Animated.View>
HEIGHT IS AN EXAMPLE (I KNOW FOR HEIGHT OR WIDTH SPECIFICALLY CAN DO THIS WITH OTHER EASIER WAY BUT I WANT TO KNOW IF I CAN DO SOMETHING LIKE THIS ABOVE, SO PLEASE ANSWER ONLY MY QUESTION, NO OTHER WAY)

Related

Animated view translateX property doesn't seem to work

I have an Animated View to which in transform prop I'm setting the translateX values. It doesn't seem to be working though. I debugged the translateX value and it seems to be updating, but the View doesn't seem to.
react-native-reanimated version: 1.13.1, react-native-gesture-handler version: 1.10.3
function Cursor() {
const translateX = new Value(0)
const gestureState = new Value(State.UNDETERMINED)
const onGestureEvent = event([{
nativeEvent: {
translationX: translateX,
state: gestureState
}
}], { useNativeDriver: true })
return (
<PanGestureHandler {...{ onGestureEvent }} onHandlerStateChange={onGestureEvent}>
<Animated.View
style={{
...StyleSheet.absoluteFillObject,
width: height,
height: height,
borderRadius: height / 2,
backgroundColor: "white",
transform: [{ translateX }],
}}
/>
</PanGestureHandler>
)
}

ReactNative: animate backgroundColor according to ScrollView scroll position

I have a ScrollView and I want to have a View with background color white, then if scroll position is more than 0 change it to transparent, and back to white if scroll goes back to 0.
I tried to get started but react native animations seem crazy complicated coming back from a vue.js background.
Here is my code:
const [animation, setAnimation] = useState(new Animated.Value(0))
const handleScroll = (event) => {
console.log(event.nativeEvent.contentOffset.y);
var scroll = event.nativeEvent.contentOffset.y;
if(scroll > 0)
{
handleAnimation();
}
};
const boxInterpolation = animation.interpolate({
inputRange: [0, 1],
outputRange:["rgb(255,255,255)" , "rgba(255,255,255,0)"]
})
const animatedStyle = {
backgroundColor: boxInterpolation
}
const handleAnimation = () => {
Animated.timing(animation, {
toValue:1,
duration: 1000,
useNativeDriver: true
}).start();
};
And my views
<ScrollView showsVerticalScrollIndicator={false} scrollEventThrottle={16} onScroll={handleScroll} style={{flex:1,backgroundColor:'white',position:'relative'}}>
<View style={{ width:'100%', height:505,position:'relative' ,backgroundColor:'red}}>
</View>
</ScrollView>
<Animated.View style={{width:'100%',height:100,...animatedStyle, flexDirection:'row',justifyContent:'space-around',alignItems:'center',position:'absolute',bottom:0,left:0}}>
</Animated.View>
The recommend way of getting the ScrollView x and y position is this below
const scrolling = React.useRef(new Animated.Value(0)).current;
<Animated.ScrollView
onScroll={Animated.event(
[{
nativeEvent: {
contentOffset: {
y: scrolling,
},
},
}],
{ useNativeDriver: false },
)}
</Animated.View>
Now everything works heres a full example (https://snack.expo.dev/#heytony01/ca5000) must be run a phone not web. And below is the code.
import React from 'react';
import { Button,View,Animated,ScrollView } from 'react-native';
export default function App() {
const scrolling = React.useRef(new Animated.Value(0)).current;
const boxInterpolation = scrolling.interpolate({
inputRange: [0, 100],
outputRange:["rgb(255,255,255)" , "rgba(255,255,255,0)"],
})
const animatedStyle = {
backgroundColor: boxInterpolation
}
return (
<View style={{flex:1}}>
<Animated.ScrollView showsVerticalScrollIndicator={false} scrollEventThrottle={16} style={{flex:1,backgroundColor:'black',position:'relative'}}
onScroll={Animated.event(
[{
nativeEvent: {
contentOffset: {
//
y: scrolling,
},
},
}],
{ useNativeDriver: false },
)}
>
<View style={{ width:'100%', height:505,position:'relative', backgroundColor:'red'}}>
</View>
</Animated.ScrollView>
<Animated.View style={{width:'100%',height:100,...animatedStyle, flexDirection:'row',justifyContent:'space-around',alignItems:'center',position:'absolute',bottom:0,left:0,zIndex:1}}>
</Animated.View>
</View>
);
}
Also if you want to print the animated values when debugging you can do this
React.useEffect(()=>{
scrolling.addListener(dic=>console.log(dic.value))
return ()=> scrolling.removeAllListeners()
},[])

React Native animations not working smooth for Flatlist pagination

I am creating pagination dots to show the flatlist content count but the issue is that translation is not smooth here is my code for the dots.
Dots.js
import React from 'react';
import {
Animated,
StyleSheet,
useWindowDimensions,
View,
ViewStyle,
} from 'react-native';
import { colors } from '../../global_constants/colors';
export interface ScalingDotProps {
data: Array<Object>;
scrollX: Animated.Value;
containerStyle?: ViewStyle;
dotStyle?: ViewStyle;
inActiveDotOpacity?: number;
inActiveDotColor?: string;
activeDotScale?: number;
activeDotColor?: string;
}
export const ScalingDot = ({
scrollX,
data,
dotStyle,
containerStyle,
inActiveDotOpacity,
inActiveDotColor,
activeDotScale,
activeDotColor,
}: ScalingDotProps) => {
const { width } = useWindowDimensions();
const defaultProps = {
inActiveDotColor: inActiveDotColor || '#347af0',
activeDotColor: activeDotColor || '#347af0',
animationType: 'scale',
inActiveDotOpacity: inActiveDotOpacity || 0.5,
activeDotScale: activeDotScale || 1.4,
};
return (
<View style={[styles.containerStyle, containerStyle]}>
{data.map((_, index) => {
const inputRange = [
(index - 1) * width,
index * width,
(index + 1) * width,
];
const colour = scrollX.interpolate({
inputRange,
outputRange: [
defaultProps.inActiveDotColor,
defaultProps.activeDotColor,
defaultProps.inActiveDotColor,
],
extrapolate: 'clamp',
});
const opacity = scrollX.interpolate({
inputRange,
outputRange: [
defaultProps.inActiveDotOpacity,
1,
defaultProps.inActiveDotOpacity,
],
extrapolate: 'clamp',
});
const scale = scrollX.interpolate({
inputRange: inputRange,
outputRange: [1, defaultProps.activeDotScale, 1],
extrapolate: 'clamp',
});
return (
<Animated.View
key={`dot-${index}`}
style={[
styles.dotStyle,
{ opacity },
{ transform: [{ scale }] },
dotStyle,
{backgroundColor: colors.primary}
// { backgroundColor: colour },
]}
/>
);
})}
</View>
);
};
const styles = StyleSheet.create({
containerStyle: {
position: 'absolute',
bottom: 20,
paddingBottom: 2,
flexDirection: 'row',
alignSelf: 'center',
},
dotStyle: {
width: 5,
height: 5,
borderRadius: 5/2,
marginHorizontal: 5,
},
});
I want animation to work on 60FPS, I used flatlist to render the data and enabled pagination.
using the scrollX value of flatlist to change the dots active status.
Solution is:
useNativeDriver: true

Update color of animated view after animation

I am creating a react native application and want to change the background color of an animated View after the user touches it. For more context, the view is in the shape of a square and I am rotating it 225 degrees when the component mounts. If the user touches the square, it will animate a flipping motion and show the other side of the square, which is a different color. The code I am using to do this can be seen below:
const app = (props) => {
let colors = [color1, color2, color3, ...];
let index = 0;
let animatedValue= new Animated.Value(0);
let squareSpin = new Animated.Value(0);
let val = 0;
useEffect(() => {
Animated.timing(squareSpin, {
toValue: 1,
duration: 3000,
easing: Easing.linear,
}).start();
}, []);
const startSpin = squareSpin.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "225deg"],
});
animatedValue.addListener(({ value }) => {
val = value;
});
let frontInt= animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ["0deg", "180deg"],
});
let backInt = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ["180deg", "360deg"],
});
let opacityFront = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [1, 0],
});
let opacityBack = animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [0, 1],
});
const flip= () => {
if (val>= 90) {
Animated.spring(animatedValue, {
toValue: 0,
friction: 6,
tension: 8,
}).start();
} else {
Animated.spring(animatedValue, {
toValue: 180,
friction: 6,
tension: 8,
}).start();
}
};
const frontAnimated = {
transform: [{ rotateY: frontInt }],
};
const backAnimated = {
transform: [{ rotateY: backInt}],
};
return (
<Animated.View
style={{transform: [{ rotate: startSpin }] }}
>
<TouchableWithoutFeedback
onPress={() => {
index++;
flip();
}}>
<Animated.View
style={[
styles.shape,
{
backgroundColor:
colors[index % colors.length],
},
frontAnimated ,
{ opacity: opacityFront },
]}
>
</Animated.View>
<Animated.View
style={[
styles.shape,
{
backgroundColor:
colors[(index + 1) % colors.length],
},
{ position: "absolute" },
backAnimated ,
{ opacity: opacityBack },
]}
></Animated.View>
</TouchableWithoutFeedback>
<Animated.View>
)
}
The Problem: The animations all work great, but the issue is that each side of the square I am flipping can only take on one color. Notice how in the colors array, there are multiple colors that the square should be based on the number of times the user presses the square. However, this is not happening and each side of the square is always the color is started out to be (color1 for the top side of the square and color2 for the bottom side of the square). I think this is happening because the view does not realize that the index is changing because it is never rendered again. Or maybe it simply cannot chance its color due to some properties of Animated.View, I am not really sure. I tried forcing a render when the square is pressed using useState but that resulted in the square to undo its rotation that happened when the component was mounted, which I do not want to happen. How do I get the background color of the views to change based on the number of taps by the user?
Thanks!
I was able to fix this by using two different color interpolation values.
let colorSideOne = colorValue1.interpolate({
inputRange: [...Array(colors.length)].map((_, index) => index),
outputRange: colors,
});
let colorSideTwo = colorValue2.interpolate({
inputRange: [...Array(colors.length)].map((_, index) => index),
outputRange: colors,
});
and with these values set color to the background of the card
<Animated.View
style={[
styles.shape,
{
backgroundColor: colorSideOne,
},
frontAnimated,
{ opacity: opacityFront },
]}>
</Animated.View>
<Animated.View
style={[
styles.shape,
{
backgroundColor: colorSideTwo,
},
frontAnimated,
{ opacity: opacityFront },
]}>
</Animated.View>
You now need to just properly update the colorValues depending on the index.
Note that you need to do this alternately for the front and back values
<TouchableWithoutFeedback
style={{
borderWidth: 2,
borderColor: 'red',
}}
onPress={() => {
index++;
//side = side === 'front' ? 'back' : 'front';
//console.log('side',side);
// console.log('index',index, 'color length', colors.length);
if (index & 1) {
colorValue1.setValue((index + 1) % colors.length);
} else {
colorValue2.setValue((index + 1) % colors.length);
}
// console.log('color value', colorValue1, colorValue2);
flip();
}}>
....
....
For the clarity iam attaching this expo demo
Hope it is what you are expecting !
Inside render method
const BackgroundColorConfig = this.Animation.interpolate(
{
inputRange: [ 0, 0.2, 0.4, 0.6, 0.8, 1 ],
outputRange: [ '#f6f6f6', '#f6f6f6', '#f6f6f6', LIGHT_RED_COLOR, LIGHT_RED_COLOR, LIGHT_RED_COLOR ]
});
usage backgroundColor:BackgroundColorConfig
give above attribute to your component(it should be under Animated.View tag)
Make a function to call Animation and use it either on componentDidMount or on button click
Define this.Animated = new Animated.value(0)
in constructor
StartBackgroundColorAnimation = (value) =>
{
Animated.timing(
this.Animation,
{
toValue: value,
duration: 600
}
).start();
}

drag and resize a shape in react-native

im new to react-native and overhelmed with all the options in the www. thats why i'm a bit confused how to complete this task in the best possible way.
i want to make something similar to this, but in react-native. A square-shape, which i can drag all over the view + resize it by dragging it's corners. I already took a look into exponent IDE and the given ThreeView-Component, but i think three.js is a bit over the top for this task, right?
[1]: http://codepen.io/abruzzi/pen/EpqaH
react-native-gesture-handler is the most appropriate thing for your case. I have created minimal example in snack. Here is the minimal code:
let FlatItem = ({ item }) => {
let translateX = new Animated.Value(0);
let translateY = new Animated.Value(0);
let height = new Animated.Value(20);
let onGestureEvent = Animated.event([
{
nativeEvent: {
translationX: translateX,
translationY: translateY,
},
},
]);
let onGestureTopEvent = Animated.event([
{
nativeEvent: {
translationY: height,
},
},
]);
let _lastOffset = { x: 0, y: 0 };
let onHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
_lastOffset.x += event.nativeEvent.translationX;
_lastOffset.y += event.nativeEvent.translationY;
translateX.setOffset(_lastOffset.x);
translateX.setValue(0);
translateY.setOffset(_lastOffset.y);
translateY.setValue(0);
}
};
return (
<View>
<PanGestureHandler onGestureEvent={onGestureTopEvent}>
<Animated.View
style={{
widht: 10,
height,
backgroundColor: 'blue',
transform: [{ translateX }, { translateY }],
}}
/>
</PanGestureHandler>
<PanGestureHandler
onGestureEvent={onGestureEvent}
onHandlerStateChange={onHandlerStateChange}>
<Animated.View
style={[
styles.item,
{ transform: [{ translateX }, { translateY }] },
]}>
<Text>{item.id}</Text>
</Animated.View>
</PanGestureHandler>
</View>
);
};
let data = [
{ key: 1, id: 1 },
];
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<FlatItem item={data[0]} />
</View>
);
}
}
Here is the snack link if you want to test! PS: I have made only top resizing. The rest is for you to do! It should be enough to understand the way how to it!