I want to animate a view's(say abcView) position based on main container flat list scroll offset. If the user scrolls down, abcView's position should move to bottom proportionately(vice versa when user scrolls back up). The position of abcView should change in proportion to the flatlist offset and not in one go. I want to use animated API but need ideas how to proceed with this.
You could try below functional component
function SomeComponent() {
const [scrollY] = useState(new Animated.Value(0));
const translateY = scrollY.interpolate({
inputRange: [0, 150],
outputRange: [0, 150],
extrapolate: 'clamp',
useNativeDriver: true,
});
return (
<View>
<Animated.FlatList
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }])}
/>
<Animated.View
style={{
transform: [{ translateY: translateY }],
}}
></Animated.View>
</View>
)
}
Since you said you need ideas, I think what you are looking for is the parallax effect. You can either go ahead building your own or you can search for such libraries e.g. react-native-parallax-header
Related
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
I am using Animated.View to change the header height.
It works well in ios but in android, when I scroll slowly the entire view is shaking.
1) First I set the state
this.state = {
scrollY:new Animated.Value(0)
}
2) Inside the render() I render the height of the view I want to animate.
const HeaderHeight = this.state.scrollY.interpolate({
inputRange: [0, 100],
outputRange: [100, 0],
extrapolate: 'clamp'
})
3) I set my Header like this:
<Animated.View style={{width:'100%', height:HeaderHeight, backgroundColor:'transparent', justifyContent:'flex-end'}}>
...
</Animated.View>
4) Inside the scrollview:
<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }])}
>
As you could see from the gif file when I slowly scroll the view the screen is shaking. This is happening in android. On ios it works fine.
Any idea how to fix this?
Any comments or advice would be really helpful :)
Your inputRange [0,100] and outputRange[100,0] have a ratio of 1.
This means that for each pixel you move in the ScrollView, your HeaderHeight will be reduced by one, which sounds great but the value you get from the scrollview event is not an integer, is a double and based on those tiny digits it will try to "aspect ratio" your outputRange and this is quite sensitive in Android and therefore the shaking.
Increase your inputRange to [0, 200] so it has a ratio of 2 related to the outputRange. This will remove the shaking.
It can be a little tricky but try to avoid set translate prop to FlatList`s style, or component that wrap Flatlist, jumps will disappear. For adding empty space before first item you can set contentContainer={{paddingTop:}}. It was in my case, hope this help someone.
Make your scrollview animated.
<Animated.ScrollView
style={{flex: 1}}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.scrollY } } }
])}
>
...
...
</Animated.ScrollView>
I have a scrollview on top of which i display a header. My goal is to animated so that when the user scrolls down, the header collapse and when the view scrolls up, the header expands.
I found plenty of exemple where the user has to scroll all the way up for the header to expand. What i'd like is for the header to expand as soon as the scroll view scrolls up.
How can I achieve this?
Here is what I have so far:
type State = { scrollY: Animated.Value };
....
headerHeight = this.state.scrollY.interpolate({
inputRange: [
0, 60
],
outputRange: [60, 0],
extrapolate: "clamp"
});
<Animated.View style={{ height: headerHeight, backgroundColor: "#F0f" }}
>
<ScreenHeader
ref={this.screenHeaderRef}
onTouchAvatar={this.handleOpenProfile}
onTouchNotifications={this.handleOpenNotification}
user={currentUser}
newNotifications={this.props.newNotifications}
/>
</Animated.View>
<WrappedComponent
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {
y:
this.state.scrollY
}
}
}
])}
onMomentumScrollEnd={this.handleMomentumScrollEnd}
{...this.props}
/>
So you are right about the animating the translateY property. So I did it a bit different way for sake of this question (Poorly, sorry about that, you need to play around a bit with this to get the right animation).
So try to set the style of the animated view's translateY to 0;
<Animated.View
style={[{
transform: [
{
translateY: this.transform
}
]
}, {
backgroundColor: 'red'
}]}
>
Then on scroll set it to -100 or whatever your header height.
later when the user comes back up, towards the top 0, you set it up to 0.
This will do the trick, hopefully.
Here's a small example, sorry for getting back late. :)
https://snack.expo.io/#subkundu/header-hideshow
So im trying to animate a view by changing the view's height when a user scrolls a specific scrollview. It looks fine on IOS although it has some jitter going on but the flickering is very visible on android. Here is my code.
Scrollview's on scroll
onScroll={(e) => {
this.state.profileTabAnimatedValue.setValue(e.nativeEvent.contentOffset.y);
}}
height interpolate object
const headerHeight = this.state.profileTabAnimatedValue.interpolate({
inputRange: [0,1],
outputRange: [1,100]
});
I will then apply the headerHeight to a view to adjust it's height
Try using the Animated event method:
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.profileTabAnimatedValue}}}]
)}
Also, if you don't want some weird behaviours like negative height or so, add this in your interpolation:
const headerHeight = this.state.profileTabAnimatedValue.interpolate({
inputRange: [0,1],
outputRange: [1,100]
extrapolate: 'clamp'
});
Last thing is you can play a bit with throttling your events firing on your ScrollView with scrollEventThrottle, just read this
I have some graphs and need to show the drawing path.
In the iOS, i can stroke the graph in CALayer with CABasicAnimation
How to do animation for stroking in React Native
To render an animated Line in a chart, you can use a charts library. One of the simplest and best-documented charts libraries in react native is - react-native-gifted-charts
It will not only make the animations easy to implement, but also let you easily customise and beautify your chart. Here is an animated Line chart made using this library. And below is the code for the same-
import { LineChart } from "react-native-gifted-charts"
const App = () => {
const lineData = [
{value: 0, dataPointText: '0'},
{value: 20, dataPointText: '20'},
{value: 18, dataPointText: '18'},
{value: 40, dataPointText: '40'},
{value: 36, dataPointText: '36'},
{value: 60, dataPointText: '60'},
{value: 54, dataPointText: '54'},
{value: 85, dataPointText: '85'}
];
return (
<View style={{backgroundColor: '#1A3461'}}>
<LineChart
initialSpacing={0}
data={lineData}
spacing={30}
textColor1="yellow"
textShiftY={-8}
textShiftX={-10}
textFontSize={13}
thickness={5}
hideRules
hideYAxisText
yAxisColor="#0BA5A4"
showVerticalLines
verticalLinesColor="rgba(14,164,164,0.5)"
xAxisColor="#0BA5A4"
color="#0BA5A4"
/>
</View>
);
};
it is an old question but I post the answer for the reference. You can use Animated from "react-native". you have to consider three directions: Horizontal, Vertical and diagonal. I added the comments to clarify the code
import React, { ReactElement, useRef, useEffect } from "react";
import { StyleSheet, Animated } from "react-native";
// size prop is to determine the size of graph
export default function Line({ size }): ReactElement {
// Pythagorean theorem to calculate the diagonal height
const diagonalHeight = Math.sqrt(Math.pow(size, 2) + Math.pow(size, 2));
// if we dont use ref, animation will be reinitialized everytime we rerender the component
// we want animation to be rendered when component reredners so we use useeffect
const animationRef = useRef<Animated.Value>(new Animated.Value(0));
useEffect(() => {
// timing is a linear animation
Animated.timing(animationRef.current, {
toValue: 1,
duration: 700,
// true might not work with the all properties that you need to animate. true might improve animation performance
useNativeDriver: false
}).start();
}, []);
return (
<>
{conditionForVerticalDisplay && (
// if we are using Animated, it has to be inside Animated.View not View
<Animated.View
style={[
// you can pass multiple style objects
{
// interpolate maps the animated value to another value
height: animationRef.current.interpolate({
inputRange: [0, 1],
outputRange: ["0%", "100%"]
})
}
]}
></Animated.View>
)}
{conditionForHorizontalDisplay && (
<Animated.View
style={[
{
height: animationRef.current.interpolate({
inputRange: [0, 1],
outputRange: ["0%", "100%"]
})
}
]}
></Animated.View>
)}
{ conditionForDiagonalDisplay && (
<Animated.View
style={[
{
height: diagonalHeight,
transform: [
{
// negative will shift it upward.
// translateY: -(diagonalHeight - size) / 2
translateY: animationRef.current.interpolate({
inputRange: [0, 1],
outputRange: [size / 2, -(diagonalHeight - size) / 2]
})
},
{
rotateZ: conditionToPosOrNegDiagonal === "MAIN" ? "-45deg" : "45deg"
}
]
}
]}
></Animated.View>
)}
</>
);
}