Initially, I have a black background with light-content barStyle but as I scroll up, the background color changes to white and I want to change the statusBar barStyle to 'dark-content'.
This is why I am trying to make my statusBar animated.
So far I have tried like this:
I made statusBar animated component
const AnimatedStatusBar = Animated.createAnimatedComponent(StatusBar)
set Animated input/output range
scroll = new Animated.Value(0)
statusBarColor = this.scroll.interpolate({
inputRange: [0, SCROLL_HEIGHT],
outputRange: ['white', 'black'],
extrapolate: "clamp"
})
conditionally change barstyle.
<AnimatedStatusBar backgroundColor='white' barStyle={this.statusBarColor ==='white'? 'light-content':'dark-content'} />
However, this is not working. How could I make the statusBar animated so that I could change the barStyle when I scroll to the top.
iOS
I changed <StatusBar/> props translucent to true
animated prop in <StatusBar/> to true
get status bar height from top value of useSafeArea hook
Create simple Animated.View having 100% width and status bar height
Android
Create AnimatedStatusBar with Animated.createAnimatedComponent(StatusBar);
Other steps are same
import { useSafeArea } from 'react-native-safe-area-context';
const AnimatedStatusBar = Animated.createAnimatedComponent(StatusBar);
function Page() {
const { top: safeAreaTop } = useSafeArea();
const barColorAnim = useRef(new Animated.Value(0)).current;
const barColor = barColorAnim.interpolate({
inputRange: [0, 1],
outputRange: ['black', 'white'],
});
const [barStyle, setBarStyle] = useState('light-content');
const toggle = () => {
setBarStyle((style) =>
style === 'light-content' ? 'dark-content' : 'light-content',
);
Animated.timing(barColorAnim, {
useNativeDriver: false,
duration: 300,
toValue: barStyle === 'light-content' ? 1 : 0,
}).start();
};
return (
<Container>
<Animated.View
style={{
width: '100%',
height: safeAreaTop,
backgroundColor: barColor,
}}
/>
<AnimatedStatusBar
animated={true}
backgroundColor={barColor}
barStyle={barStyle}
translucent={true}
/>
<Button title="Toggle" onPress={toggle} />
</Container>
);
}
import { View, TouchableOpacity, Animated, StatusBar, Text } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
const AnimatedStatusBar = Animated.createAnimatedComponent(StatusBar);
const barColorAnim = new Animated.Value(0);
const barColor = barColorAnim.interpolate({
inputRange: [0, 1],
outputRange: ['black', 'white'],
});
export class App extends React.Component {
this.state = {
barStyle: 'light-content'
};
render() {
const toggle = () => {
this.setState({
barStyle: this.state.barStyle === 'light-content' ? 'dark-content' : 'light-content'
})
Animated.timing(barColorAnim, {
useNativeDriver: false,
duration: 300,
toValue: this.state.barStyle === 'light-content' ? 1 : 0,
}).start();
};
return (
<>
<SafeAreaProvider>
<AnimatedStatusBar
animated={true}
backgroundColor={barColor}
barStyle={this.state.barStyle}
translucent={true}
/>
<TouchableOpacity
onPress={toggle}><Text>Click toggle</Text></TouchableOpacity>
</SafeAreaProvider>
</>
);
}
}
Related
I have a React Native component rendering a notification text. I want the text to fade in and fade out after a small delay. When a new notification text is set, I want to restart the same animation.
I have the following code:
export default function Notification({ notification }) {
if (!notification) {
return null;
}
const [fadeAnimation] = useState(new Animated.Value(0));
useEffect(() => {
Animated.sequence([
Animated.timing(fadeAnimation, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
Animated.delay(3000),
Animated.timing(fadeAnimation, {
toValue: 0,
duration: 500,
useNativeDriver: true,
})]).start()
}, []);
return (
<Animated.View style={{ opacity: fadeAnimation, }}>
<View style={styles.notificaton}>
<Text style={styles.text}>{notification}</Text>
</View>
</Animated.View >
)
}
I read that I should be able to reset the animation with setValue(0) again, however I do not know where and when to call it.
work for me
import React, { Component } from 'react';
import { Animated, View } from 'react-native';
class Testing extends Component {
constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
}
componentDidUpdate(prevProps) {
if (prevProps.myProp !== this.props.myProp) {
this.startAnimation();
}
}
startAnimation() {
this.animatedValue.setValue(0);
Animated.timing(this.animatedValue, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}
render() {
const { myProp } = this.props;
const opacity = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
});
return (
<View>
<Animated.Text style={{ opacity }}>
My prop val : {myProp}
</Animated.Text>
</View>
);
}
}
export default Testing;
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()
},[])
I am trying to rotate an icon on press of a button in my react native application.
But I am getting this error:
Error while updating property 'transform' of a view managed by:
RCTView
This is the Icon itself:
<TouchableOpacity
style={[
styles.expandButton,
{transform: [{rotateX: toString(expandIconAngle) + 'deg'}]},
]}
onPress={() => {
rotateIcon();
}}>
<Icon name="expand-less" color="#ffffff" size={28} />
</TouchableOpacity>
This is the function which is responsible for rotating the icon:
const expandIconAngle = useRef(new Animated.Value(0)).current;
function rotateIcon() {
Animated.timing(expandIconAngle, {
toValue: 180,
duration: 300,
easing: Easing.linear,
}).start();
}
Where am I going wrong?
use interpolate and Animated.Image :
import React, { useRef } from "react";
import { Animated, Text, View, StyleSheet, Button, SafeAreaView,Easing,TouchableOpacity } from "react-native";
const App = () => {
// fadeAnim will be used as the value for opacity. Initial Value: 0
const angle = useRef(new Animated.Value(0)).current;
const fadeOut = () => {
// Will change fadeAnim value to 0 in 3 seconds
Animated.timing(
angle,
{
toValue: 1,
duration: 3000,
easing: Easing.linear, // Easing is an additional import from react-native
useNativeDriver: true // To make use of native driver for performance
}
).start()
};
const spin =angle.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
return (
<SafeAreaView style={styles.container}>
<Animated.Image
style={{transform: [{rotateX: spin}] }}
source={require('#expo/snack-static/react-native-logo.png')} />
<TouchableOpacity onPress={fadeOut} style={styles.buttonRow}>
<Text>Button</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
buttonRow: {
flexBasis: 100,
justifyContent: "space-evenly",
marginVertical: 16
}
});
export default App;
LIVE example - https://snack.expo.dev/TP-fQExXT
I want to implement an animated accordion list/ drawer / drop-down menu / collapsible card.
The animation should be performant and look like this:
After a lot of searching, I could find many libraries. But I wanted to implement it without any library. Also, some tutorials showed how to build one, but they were not performant.
Finally, this is how I implemented it. The complete snack code is here: https://snack.expo.dev/#vipulchandra04/a85348
I am storing isOpen (whether the menu is open or closed) in a state. Then changing that state on button press. I am using the LayoutAnimation API in React-Native to animate the opening and closing of the list. LayoutAnimation runs the animation natively, thus it is performant.
const Accordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleOpen = () => {
setIsOpen(value => !value);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
return (
<>
<TouchableOpacity onPress={toggleOpen} activeOpacity={0.6}>
{title}
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</>
);
};
const styles = StyleSheet.create({
hidden: {
height: 0,
},
list: {
overflow: 'hidden'
},
});
With this, it will fix the Vipul's demo's error: if click accordion so fast, children's opacity descending to 0. and add animation for icon
import {
Animated,
LayoutAnimation,
Platform,
StyleProp,
StyleSheet,
UIManager,
View,
ViewStyle,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons;
if (
Platform.OS === 'android' &&
UIManager.setLayoutAnimationEnabledExperimental
) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const toggleAnimation = duration => {
return {
duration: duration,
update: {
property: LayoutAnimation.Properties.scaleXY,
type: LayoutAnimation.Types.easeInEaseOut,
},
delete: {
property: LayoutAnimation.Properties.opacity,
type: LayoutAnimation.Types.easeInEaseOut,
},
};
};
interface IAccordion {
title?: JSX.Element | JSX.Element[];
children?: JSX.Element | JSX.Element[];
style?: StyleProp<ViewStyle> | undefined;
}
const Accordion = ({title, children, style}: IAccordion) => {
const [isOpen, setIsOpen] = useState(false);
const animationController = useRef(new Animated.Value(0)).current;
const arrowTransform = animationController.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '90deg'],
});
const onToggle = () => {
setIsOpen(prevState => !prevState);
const duration = 300;
const config = {
duration: duration,
toValue: isOpen ? 0 : 1,
useNativeDriver: true,
};
Animated.timing(animationController, config).start();
LayoutAnimation.configureNext(toggleAnimation(duration));
};
return (
<View style={style ? style : styles.accordion}>
<TouchableOpacity onPress={onToggle} style={styles.heading}>
{title}
<Animated.View style={{transform: [{rotateZ: arrowTransform}]}}>
<Ionicons name={'chevron-forward-outline'} size={18} />
</Animated.View>
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</View>
);
};
export default Accordion;
I had difficulty using the native API, so I go to third parties. The only thing I couldn't do was make the accordion size automatic.
import { useEffect } from 'react';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
Easing,
} from 'react-native-reanimated';
import styled from 'styled-components';
const Accordion = ({ children, open, height }) => {
const heightAnimation = useSharedValue(0);
useEffect(() => {
if (open === true) heightAnimation.value = height;
if (open === false) heightAnimation.value = 0;
}, [open]);
const animatedStyle = useAnimatedStyle(() => {
return {
height: withTiming(heightAnimation.value, {
duration: 500,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
}),
};
});
return (
<>
<Main style={animatedStyle}>{children}</Main>
</>
);
};
const Main = styled(Animated.View)`
width: 100%;
align-items: center;
justify-content: center;
overflow: hidden;
`;
export default Accordion;
Using:
<Accordion height={height} open={open}>
{children}
</Accordion>
As asked here for an example of what I managed to do with it, I tried to get as much out of it as possible.
You can see a deploy here: https://snack.expo.dev/#francisco.ossian/accordion
Libs used, react-native-reanimated
I use Animated.Text for change Animation Text but it's not working properly
I also require in animation fade out old text & fade in the new text.
import React, { Component, PropTypes } from 'react';
import {
StyleSheet,
View,
Text,
Image,
Dimensions,
Animated
} from 'react-native';
import styles from './styles';
const win = Dimensions.get('window');
export default class Logo extends Component {
constructor(props) {
super(props);
this.tempText = new Animated.Value("Hello");
}
componentWillMount () {
Animated.timing(this.tempText, {
duration: 5000,
toValue: "New Text",
}).start();
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.tempText}
</Animated.Text>
</View>
);
}
}
Actual output Get - Change text after 5 Second but it's not working.please help me.
What you're trying to achieve can be done without using Animated at all, and actually, Animated isn't intended for this particular use.
Replacing the text can be done with a simple variable, and the text change can be triggered by a setTimeout.
Animated is intended for changing a numeric value, not a text value. Think of it this way - if the change is supposed to happen over a 5 second interval, what would the mid-value be?
Do this instead:
export default class Logo extends Component {
constructor(props) {
super(props);
this.state = {text: "Hello"};
}
componentDidMount () {
setTimeout(() => this.setState({text: "New Text"}), 5000);
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.state.text}
</Animated.Text>
</View>
);
}
}
My example with smoothly opacity animation.
Sorry, without fadeIn, fadeOut.
const inputRange = [0, 1, 2, 3];
const AnimatedText = Animated.createAnimatedComponent(Text);
const animationProps = {
duration: 500,
easing: Easing.out(Easing.linear),
isInteraction: false,
useNativeDriver: true,
};
class Onboarding extends PureComponent {
activeDot = 0;
animationDot = new Animated.Value(0);
toggleOnButton = () => {
Animated.timing(this.animationDot, {
toValue: this.activeDot + 1,
...animationProps,
}).start((endState) => {
if (endState.finished) {
this.activeDot = this.activeDot + 1;
}
});
}
renderButton = () => {
const opacityNext = this.animationDot.interpolate({
inputRange,
outputRange: [1, 1, 1, 0]
});
const opacityGetStarted = this.animationDot.interpolate({
inputRange,
outputRange: [0, 0, 0, 1]
});
return (
<TouchableOpacity style={styles.button} onPress={this.toggleOnButton}>
<AnimatedText style={[styles.buttonText, { opacity: opacityNext }]}>
Next
</AnimatedText>
<AnimatedText style={[styles.buttonText, {
top: normalize(isIOS ? 12 : 8), position: 'absolute', opacity: opacityGetStarted
}]}
>
Get started
</AnimatedText>
</TouchableOpacity>
);
}
}