I'm trying to make header that will animate from transparent to solid opaque color upon scrolling down using in React-Native React Navigation 5.
Starts to transition to opaque when scrolling halfway
Becomes fully opaque when reach the maximum offset
You can do this by setting the header style opacity to an animated value.
First define your animated value, we'll interpolate the yOffset to get the opacity desired.
const yOffset = useRef(new Animated.Value(0)).current;
const headerOpacity = yOffset.interpolate({
inputRange: [0, 200],
outputRange: [0, 1],
extrapolate: "clamp",
});
then you want to attach an animated.event listener to an animated scroll view
<Animated.ScrollView
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: yOffset,
},
},
},
],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
>
Your content should be inside the scroll view
In your screen add a on mount or use effect where you set the animatedValue as the header opacity
useEffect(() => {
navigation.setOptions({
headerStyle: {
opacity: headerOpacity,
},
headerBackground: () => (
<Animated.View
style={{
backgroundColor: "white",
...StyleSheet.absoluteFillObject,
opacity: headerOpacity,
}}
/>
),
headerTransparent: true,
});
}, [headerOpacity, navigation]);
I've used header transparent and header background so that the background component changes also.
Here is an example:
https://snack.expo.io/#dannyhw/react-navigation-animated-header
const handleScroll = () => {
YourElementRef.current.style.backgroundColor = `rgba(245, 245, 245, ${window.scrollY > 300 ? 1 : '0.' + (window.scrollY * 3)})`;
}
window.addEventListener('scroll', handleScroll, true);
You need to add scroll listener and call function that animates it.
The scroll element is represented by a ref stuff. e.g.
const YourElementRef = React.useRef(null);
<SomeElement ref={YourElementRef}...
My apps memory usage is increasing by 0,1 MB about every 3 seconds without me doing anything in the app. I made sure to remove all event listeners so that's not the problem, im out of tricks to solve this memory leak. Is there a tool to inspect which processes are writing to the ram or some other way to detect this leak ?
I have detected the memory leak, it was an issue with react-native-gesture-handler, I did this:
<PanGestureHandler
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onGestureEvent}>
<Animated.View style={{ transform: [{ translateX: this.translateX }] }}>
<FlatList />
</Animated.View>
</PanGestureHandler>
I didn't thought about that there is going to be a gesture handler in front of the whole FlatList which in my case contains 200+ items. I still don't ge why the memory usage is increasing without doing anything but I have resolved this issue.
This is my workaround:
const { width: SCREEN_WIDTH } = Dimensions.get('window');
const TOSS_SEC = 0.2;
const MULTIPLIER = Math.round(SCREEN_WIDTH / 90);
class ReanimatedFlatList extends React.Component {
constructor(props) {
super(props);
// drag Distance
const dragX = new Value(0);
// gesture state
const state = new Value(-1);
// drag velocity
const dragVX = new Value(0);
this.onGestureEvent = event([
{ nativeEvent: { translationX: dragX, velocityX: dragVX, state: state } },
]);
this.transX = new Value();
const prevDragX = new Value(0);
const clock = new Clock();
const snapPoint = cond(
lessThan(add(this.transX, multiply(TOSS_SEC, dragVX)), -80),
-100,
0,
);
this.unconstrainedX = cond(
eq(state, State.ACTIVE),
[
stopClock(clock),
set(this.transX, add(this.transX, sub(dragX, prevDragX))),
set(prevDragX, dragX),
this.transX,
],
[
set(prevDragX, 0),
set(
this.transX,
cond(
defined(this.transX),
runSpring(clock, this.transX, dragVX, snapPoint),
0,
),
),
],
);
this.translateX = interpolate(this.unconstrainedX, {
inputRange: [-100, 0],
outputRange: [-100, 0],
extrapolate: Extrapolate.CLAMP,
});
}
render() {
return (
<React.Fragment>
<Animated.View style={{ transform: [{ translateX: this.translateX }] }}>
<FlatList />
</Animated.View>
<PanGestureHandler
maxPointers={1}
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onGestureEvent}>
<Animated.View
style={{
transform: [{ translateX: multiply(this.translateX, MULTIPLIER) }],
position: 'absolute',
top: 0,
width: SCREEN_WIDTH,
right: -SCREEN_WIDTH + 50,
bottom: 0,
}}
/>
</PanGestureHandler>
</React.Fragment>
);
}
}
This allows me to move the FlatList 100 pt to the left and display something next to it much like a navigation drawer. This solution is not perfect because you are not going to be able to scroll between SCREEN_WIDTH - 50 pt and SCREEN_WIDTH on the x axis but I haven't found a better solution for now.
I'm trying to build something similar to IMessage's and WhatsApp's header in react native, where users can pull down to reveal a search bar in the header.
I have been able to pull down to reveal a hidden input, but because the scrollview's y value becomes negative on pull, it will bounce back to y = 0 and prevent the input from sticking to the top. I have tried using both translateY and scaleY to reveal the hidden input.
class List extends Component {
scrollY = new Animated.Value(0)
render() {
const translateY = this.props.scrollY.interpolate({
inputRange: [ -50, 0 ],
outputRange: [ 50, 0 ],
extrapolate: 'clamp',
})
return (
<>
<Animated.View style={[
styles.container,
{ transform: [ { translateY } ] },
]}>
<Input />
</Animated.View>
<Animated.ScrollView
onScroll={Animated.event(
[ { nativeEvent: { contentOffset: { y: this.scrollY } } } ],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
>
{...}
</Animated.ScrollView>
</>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: colors.white,
width: windowWidth,
height: 50,
position: 'absolute',
top: -50,
zIndex: -99,
},
});
I found this Stack Overflow post that has been useful to reference but it is IOS specific Pull down to show view
I solved this by using contentOffset and without any animations. I needed to make sure the scrollview was at least the size of the phone's windowHeight and then used contentOffset to push the initial y value of the Scrollview to the size of the header
<ScrollView
ListHeaderComponent={() => (
<Header headerHeight={hiddenHeaderHeight} />
)}
contentContainerStyle={{ minHeight: windowHeight }}
contentOffset={{ y: hiddenHeaderHeight }}
...
This solution works for a Flatlist as well.
One thing to note is contentOffset is an ios specific prop
check out this medium article. It provides a detailed explanation of how to do something similar to your desired behavior.
I am trying to import react-spring animation library to a reactjs application which is based on react component classes.
It seems that new (as of 2019) React Hooks made some integration messier.
So that is why I am asking how to use react-spring which in turn uses react hooks, in a ReactJS application what uses classes.
The code that does not work properly looks like:
import React from 'react';
import { useSpring, animated, interpolate } from 'react-spring'
export default class TestAnimation extends React.Component {
constructor(props) {
super(props);
const { o, xyz, color } = useSpring({
from: { o: 0, xyz: [0, 0, 0], color: 'red' },
o: 1,
xyz: [10, 20, 5],
color: 'green'
});
this.aniText = <animated.div
style={{
// If you can, use plain animated values like always, ...
// You would do that in all cases where values "just fit"
color,
// Unless you need to interpolate them
background: o.interpolate(o => `rgba(210, 57, 77, ${o})`),
// Which works with arrays as well
transform: xyz.interpolate((x, y, z) => `translate3d(${x}px, ${y}px, ${z}px)`),
// If you want to combine multiple values use the "interpolate" helper
border: interpolate([o, color], (o, c) => `${o * 10}px solid ${c}`),
// You can also form ranges, even chain multiple interpolations
padding: o.interpolate({ range: [0, 0.5, 1], output: [0, 0, 10] }).interpolate(o => `${o}%`),
// Interpolating strings (like up-front) through ranges is allowed ...
borderColor: o.interpolate({ range: [0, 1], output: ['red', '#ffaabb'] }),
// There's also a shortcut for plain, optionless ranges ...
opacity: o.interpolate([0.1, 0.2, 0.6, 1], [1, 0.1, 0.5, 1])
}}
>
{o.interpolate(n => n.toFixed(2)) /* innerText interpolation ... */}
</animated.div>
};
render() {
return <div>
{this.aniText}
</div>;
}
}
which results this error:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
You can't use hooks inside class components. So, you could either split out the animated component into its own functional component, which would look like this:
import React from 'react';
import { useSpring, animated, interpolate } from 'react-spring'
const AniText = ()=> {
const { o, xyz, color } = useSpring({
from: { o: 0, xyz: [0, 0, 0], color: 'red' },
o: 1,
xyz: [10, 20, 5],
color: 'green'
});
return (<animated.div
style={{
// If you can, use plain animated values like always, ...
// You would do that in all cases where values "just fit"
color,
// Unless you need to interpolate them
background: o.interpolate(o => `rgba(210, 57, 77, ${o})`),
// Which works with arrays as well
transform: xyz.interpolate((x, y, z) => `translate3d(${x}px, ${y}px, ${z}px)`),
// If you want to combine multiple values use the "interpolate" helper
border: interpolate([o, color], (o, c) => `${o * 10}px solid ${c}`),
// You can also form ranges, even chain multiple interpolations
padding: o.interpolate({ range: [0, 0.5, 1], output: [0, 0, 10] }).interpolate(o => `${o}%`),
// Interpolating strings (like up-front) through ranges is allowed ...
borderColor: o.interpolate({ range: [0, 1], output: ['red', '#ffaabb'] }),
// There's also a shortcut for plain, optionless ranges ...
opacity: o.interpolate([0.1, 0.2, 0.6, 1], [1, 0.1, 0.5, 1])
}}
>
{o.interpolate(n => n.toFixed(2)) /* innerText interpolation ... */}
</animated.div>)
}
export default class TestAnimation extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>
<AniText />
</div>;
}
}
OR, if you want to stick with a class component, react-spring exports a render-props API as well, which is completely valid inside any React component, class or otherwise:
import React from "react";
import { Spring, animated, interpolate } from "react-spring/renderprops";
export default class TestAnimation extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Spring
native
from={{ o: 0, xyz: [0, 0, 0], color: "red" }}
to={{ o: 1, xyz: [10, 20, 5], color: "green" }}
>
{({ o, xyz, color }) => (
<animated.div
style={{
// If you can, use plain animated values like always, ...
// You would do that in all cases where values "just fit"
color,
// Unless you need to interpolate them
background: o.interpolate(o => `rgba(210, 57, 77, ${o})`),
// Which works with arrays as well
transform: xyz.interpolate(
(x, y, z) => `translate3d(${x}px, ${y}px, ${z}px)`
),
// If you want to combine multiple values use the "interpolate" helper
border: interpolate(
[o, color],
(o, c) => `${o * 10}px solid ${c}`
),
// You can also form ranges, even chain multiple interpolations
padding: o
.interpolate({ range: [0, 0.5, 1], output: [0, 0, 10] })
.interpolate(o => `${o}%`),
// There's also a shortcut for plain, optionless ranges ...
opacity: o.interpolate([0.1, 0.2, 0.6, 1], [1, 0.1, 0.5, 1])
}}
>
{// Finally, this is how you interpolate innerText
o.interpolate(n => n.toFixed(2))}
</animated.div>
)}
</Spring>
</div>
);
}
}
Here is a codesandbox with the two solutions side-by-side:
https://codesandbox.io/s/8ynxyowzk0
I'm trying to create some code that increases the size of a button when the user holds it down then reverts back to the initial size when released.
I'm relatively new to React/RN and have searched tons of websites to find the result, but can't seem to find anything.
I can't tell whether I should be using PanResponder here or not. I also tried using Animated.timing, but the timing is hard-coded & not bound to the length of time that the user holds down the button. I tried Animated.spring, but again that's not bound to length of time that the user holds the button down.
I'll post a quick gif that replicates what I'm trying to go for.
https://imgur.com/a56pSQl
Here's what I have so far:
this.scaleAnimation = new Animated.value(3)
handlePress = () => {
Animated.spring(this.scaleAnimation, {
toValue: 4,
friction: 2,
tension: 160
}).start()
}
render() {
const pauseStyle = {
transform: [
{ scale: this.scaleAnimation }
]
}
return (
<TouchableWithoutFeedback onPress={this.handlePress}>
<Animated.View style={[ pauseStyle ]}>
<Ionicons name="md-pause" />
</Animated.View>
</TouchableWithoutFeedback>
)
}
Any takes are greatly appreciated :D
Please find the detailed answer with code snippets.
Add this to constructor of component
this.handlePressIn = this.handlePressIn.bind(this);
this.handlePressOut = this.handlePressOut.bind(this);
this.animatedValue = new Animated.Value(1);
This method is for scaling button with animation when button gets pressed
handlePressIn() {
Animated.spring(this.animatedValue, {
toValue: 3,
friction: 3,
tension: 40
}).start();
}
This method is for resetting button to its initial scale with animation when touch gets released
handlePressOut() {
Animated.spring(this.animatedValue, {
toValue: 1,
friction: 3,
tension: 40
}).start();
}
Render method
render() {
const animatedStyle = {
transform: [{ scale: this.animatedValue }]
}
return (
<TouchableWithoutFeedback
onPressIn={this.handlePressIn}
onPressOut={this.handlePressOut}
>
<Animated.View style={animatedStyle}>
<Text style={styles.text}>Press Me</Text>
</Animated.View>
</TouchableWithoutFeedback>
);
}
You can use different types of animations. Please find the link for reference:
https://facebook.github.io/react-native/docs/animated#configuring-animations