I am trying to animate border color in React Native, but animation doesn't work. Border color doesn't have ORIGINAL_COLOR = '#a0a0a0' neither SUCCESS_COLOR = '#008FEB', it is black. How can I make default color ORIGINAL_COLOR = '#a0a0a0' if keyboard is hidden and SUCCESS_COLOR = '#008FEB' when keyboard shows up?
const styles = StyleSheet.create({
inputContainer: {
borderBottomWidth: 1,
},
});
<Input
containerStyle={styles.inputContainer}
underlineColorAndroid="transparent"
/>;
Input.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { TextInput, Text, View, Animated, Keyboard } from 'react-native';
import styles from './styles';
const SUCCESS_COLOR = '#008FEB';
const ORIGINAL_COLOR = '#a0a0a0';
export default class Input extends Component {
constructor(props) {
super(props);
this.color = new Animated.Value(ORIGINAL_COLOR);
}
componentWillMount () {
this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardWillShowSub.remove();
this.keyboardWillHideSub.remove();
}
keyboardWillShow = (event) => {
console.log(SUCCESS_COLOR);
Animated.timing(this.color, {
duration: event.duration,
toValue: SUCCESS_COLOR,
}).start();
};
keyboardWillHide = (event) => {
console.log(ORIGINAL_COLOR);
Animated.timing(this.color, {
duration: event.duration,
toValue: ORIGINAL_COLOR,
}).start();
};
render() {
const {
value,
} = this.props;
return (
<Animated.View style={[styles.containerStyle, { borderBottomColor: this.color }]}>
<TextInput
ref="input"
{...this.props}
value={value}
/>
</Animated.View>
);
}
}
Here you go: https://snack.expo.io/#zvona/interpolation-of-color
The key is to use interpolate to change the number value into rgb value:
let borderBottomColor = this.color.interpolate({
inputRange: [0, 1],
outputRange: [ORIGINAL_COLOR, SUCCESS_COLOR]
});
Related
I am new to programming and my task is to animate the height of my image on scroll. i.e. decrease height on scroll down and increase height to original when scrolling up. However, following the React Native documentation on Animated, I replaced Text component with my Image and I'm unable to get the image showing. It shows when I don't wrap it in <Animated.View>, can anyone explain why and tell me how to fix? Thank you.
This is my current code just trying to do the sample fade animation before I try to animate height and running into issues getting the TopImage component showing:
import React, { useRef } from 'react';
import {
Animated,
View,
ScrollView,
StyleSheet,
} from 'react-native';
import { useHeaderHeight } from '#react-navigation/stack';
import TopImage from '../components/TopImage';
import GroceryList from '../components/GroceryList';
const App = () => {
const { theme } = useContext(ThemeContext);
const styles = createStyleSheet();
const fadeAnim = useRef(new Animated.Value(0)).current;
const fadeIn = () => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 5000,
useNativeDriver: true,
}).start();
};
const fadeOut = () => {
Animated.timing(fadeAnim, {
toValue: 0,
duration: 3000,
useNativeDriver: true,
}).start();
};
return (
<View style={styles.container}>
<Animated.View
style={
{
// Bind opacity to animated value
opacity: fadeAnim,
},
}>
<TopImage />
</Animated.View>
<ScrollView
style={styles.scrollContainer}
onScrollBeginDrag={() => fadeIn()}>
<GroceryList />
</ScrollView>
</View>
);
};
const createStyleSheet = () => {
const headerHeight = useHeaderHeight();
return StyleSheet.create({
container: { flex: 1 },
scrollContainer: { paddingTop: headerHeight + 100 },
});
};
export default App;
and this is the code for my TopImage component:
import React from 'react';
import { Image, StyleSheet } from 'react-native';
const topImage = require('../images/top.png');
const TopImage = () => {
const styles = createStyleSheet();
return <Image source={topImage} style={styles.image} />;
};
const createStyleSheet = () => {
return StyleSheet.create({
image: {
position: 'absolute',
width: '100%',
height: '50%',
},
});
};
export default TopImage;
The reason it's not showing is because your image is 'absolute' positioned and as such, the outer container (Animated.View) has no height.
If you apply a height to the Animated.View like below. You'll see that the opacity is actually working, you just couldn't see it before because the Animated.View was 0 pixels tall.
<Animated.View
style={
{
// Bind opacity to animated value
opacity: fadeAnim,
height: 300,
},
}>
<TopImage />
</Animated.View>
Alternatively you could make the image position relative, height 0 and animate it to the correct size.
Using react-native-blur / react-native-community/blur, is it possible to set the blurred item to be white?
I tried the following but it isn't totally white:
<BlurView
blurType={'xlight'}
blurAmount={100}
/>
Use react-native-blur and create blur component like
// components/Blur.js
import React, { Component } from "react";
import { Animated, View, Platform, Easing, StyleSheet } from "react-native";
import { BlurView } from "#react-native-community/blur";
import PropTypes from "prop-types";
import styles from "./styles";
export default class Blur extends Component {
constructor(props) {
super(props);
this.state = {
fadeAnimation: new Animated.Value(0),
};
}
static propTypes = {
title: PropTypes.string.isRequired,
};
fadeIn = () => {
Animated.timing(this.state.fadeAnimation, {
toValue: 1,
duration: 3000,
}).start();
};
fadeOut = () => {
Animated.timing(this.state.fadeAnimation, {
toValue: 0,
duration: 3000,
}).start();
};
componentDidMount() {
this.fadeIn();
}
render() {
return (
<BlurView
style={styles.blur}
blurType="light"
blurAmount={10}
reducedTransparencyFallbackColor="white"
/>
);
}
}
const styles = StyleSheet.create({
blur: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
justifyContent: "center",
backgroundColor: "rgba(100,100,100, 0.5)",
padding: 20,
},
});
Usage in screen like
import { Blur } from "../components";
export default class App extends Component {
render() {
return (
<View style={{flex:1}}>
<!-- Render views which should be blur -->
<View>
.....
</View>
<!-- render blur view in the end -->
<Blur />
</View>
);
}
}
If you want it to be TOTALLY white, then why mnot just set the background color to white and do not use a blur at all?
I'm trying to create a button that resizes (gets a little bit smaller when it' pressed). I use TouchableWithoutFeedback from react-native-gesture-handlerand I use react-native-reanimated.
This is my code so far:
import React, { useState } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
const { interpolate, sub } = Animated;
const TouchableResize = (props) => {
const { onPress, children } = props;
const [scale, setScale] = useState(0);
const scaling = interpolate(scale, {
inputRange: [0, 1],
outputRange: [1, 0.90],
extrapolate: Extrapolate.CLAMP
});
return (
<TouchableWithoutFeedback onPressIn={() => setScale(1)} onPressOut={() => setScale(0)}>
<Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
};
export { TouchableResize };
This code works partly. The button resizes to 0.90 when it's pressed, but the animation is not smooth. It snaps directly to 0.90, and when it's released, the button directly snaps back.
How can I update my code so the animation runs smoothly? Please note I'm a complete beginner in react-native-reanimated.
You have to use timing function to change your Animated.Value over time. Here example in docs. Also, I created expo snack example. Here updated component code
import React, { useState, useMemo } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
const {
Clock,
Value,
set,
cond,
startClock,
clockRunning,
timing,
debug,
stopClock,
block,
interpolate,
useCode,
} = Animated;
function runTiming(clock, from, to) {
const state = {
finished: new Value(0),
position: new Value(from),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 100,
toValue: new Value(to),
easing: Easing.inOut(Easing.ease),
};
return block([
cond(
clockRunning(clock),
[],
startClock(clock),
),
// we run the step here that is going to update position
timing(clock, state, config),
// if the animation is over we stop the clock
cond(state.finished, debug('stop clock', stopClock(clock))),
// we made the block return the updated position
state.position,
]);
}
const TouchableResize = (props) => {
const { onPress, children } = props;
const [pressed, setPressed] = useState(false);
const {clock, scale} = useMemo(() => ({
clock: new Clock(),
scale: new Value(1),
}), [])
useCode(
() => block([
pressed ? set(scale, runTiming(clock, 0, 1)) : set(scale, runTiming(clock, 1, 0))
]), [pressed]
);
const scaling = interpolate(scale, {
inputRange: [0, 1],
outputRange: [1, 0.90],
extrapolate: Extrapolate.CLAMP
});
return (
<TouchableWithoutFeedback onPressIn={() => setPressed(true)} onPressOut={() => setPressed(false)}>
<Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
};
export { TouchableResize };
I create a class component with animation for common button:
import React, { Component } from 'react';
import { Text, TouchableOpacity, StyleSheet, Animated, Easing, View } from 'react-native';
class AnimatedPrimaryButton extends Component {
constructor(props) {
super(props);
this.state = {
toggle: false,
animated: new Animated.Value(0)
}
}
animatedButton = (toggle) => {
this.state.animated.setValue(toggle ? 1 : 0);
Animated.timing(this.state.animated, {
toValue: toggle ? 0 : 1,
duration: 250,
easing: Easing.bounce
}).start();
this.setState({ toggle: !toggle });
}
render() {
const { toggle, animated } = this.state;
const { onPress, disabled, width, height } = this.props;
return (
<TouchableOpacity
disabled={disabled}
onPress={onPress}
style={[styles.buttonStyle, { width, height }]}
>
<Animated.View style={{
// other styles
transform: [{ scale: animated.interpolate({ inputRange: [0, 1], outputRange: [0, 1]})
}
]
}}>
</Animated.View>
<Text style={[styles.textStyle, { fontSize }]}>
{children}
</Text>
</TouchableOpacity>
);
};
}
const styles = StyleSheet.create({
// some styles
});
export default AnimatedPrimaryButton;
I use the create component on other screen like:
import AnimatedPrimaryButton from '../Shared/Button/AnimatedPrimaryButton';
doSomething = () => {
// do something...
}
render() {
return (
<View>
<AnimatedPrimaryButton
onPress={() => this.doSomething()}
width={400}
height={57}
fontSize={20}
backgroundColor={confirmButtonBg}
disabled={disabledConfirmButton}
>
{I18n.t('SIGN_IN_BUTTON')}
</AnimatedPrimaryButton>
</View>
);
}
Now I want to use doSomething function and trigger animatedButton at the same time.
In some conditions my disable will switch true or false, so I try to set the code on my AnimatedPrimaryButton is not working.
onPress={() => !disabled ? this.animatedButton(toggle) : onPress}
It looks like use the props onPress under arrow function won't work.
How to use doSomething and animatedButton function on class component AnimatedPrimaryButton ?
Any help would be appreciated.
in AnimatedPrimaryButton component you can make a onPress function
onPress(){
this.props.onPress();
this. animatedButton();
}
and rest you are sending the doSomething() function correctly on onPress while calling AnimatedPrimaryButton on other screen.
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>
);
}
}