How to make Animated timing/spring refresh with the new start? - react-native

I was trying to include the Circular Progress module to my project. Everything work well except that When the Fill value change from 100 to 0 ,the Animated.spring/ timing function will draw back. Is there any idea to make circular progress bar refresh with the start point when it hit end point?
import React, { PropTypes } from 'react';
import { View, Animated } from 'react-native';
import CircularProgress from './CircularProgress';
const AnimatedProgress = Animated.createAnimatedComponent(CircularProgress);
export default class AnimatedCircularProgress extends React.Component {
constructor(props) {
super(props);
this.state = {
chartFillAnimation: new Animated.Value(props.prefill || 0)
}
}
componentDidMount() {
this.animateFill();
}
componentDidUpdate(prevProps) {
if (prevProps.fill !== this.props.fill) {
this.animateFill();
}
}
animateFill() {
const { tension, friction } = this.props;
Animated.spring(
this.state.chartFillAnimation,
{
toValue: this.props.fill,
tension,
friction
}
).start();
}
performLinearAnimation(toValue, duration) {
Animated.timing(this.state.chartFillAnimation, {
toValue: toValue,
duration: duration
}).start();
}
render() {
const { fill, prefill, ...other } = this.props;
return (
<AnimatedProgress
{...other}
fill={this.state.chartFillAnimation}
/>
)
}
}
AnimatedCircularProgress.propTypes = {
style: View.propTypes.style,
size: PropTypes.number.isRequired,
fill: PropTypes.number,
prefill: PropTypes.number,
width: PropTypes.number.isRequired,
tintColor: PropTypes.oneOf([PropTypes.string, PropTypes.object]),
backgroundColor: PropTypes.oneOf([PropTypes.string, PropTypes.object]),
tension: PropTypes.number,
friction: PropTypes.number
}
AnimatedCircularProgress.defaultProps = {
tension: 7,
friction: 10
};

.start() takes an optional callback which will be called once the animation is finished, so you can recursively call your animation:
animateFill() {
const { tension, friction } = this.props;
this.state.charFillAnimation.setValue(0); //reset to 0
Animated.spring(
this.state.chartFillAnimation,
{
toValue: this.props.fill,
tension,
friction
}
).start(() => this.animateFill());
}

Related

how to drag the view in react native

I have an example code I put it on below ,this example show the circle to drag another location
import React, { Component } from "react";
import {
StyleSheet,
View,
PanResponder,
Animated
} from "react-native";
export default class Draggable extends Component {
constructor() {
super();
this.state = {
pan: new Animated.ValueXY()
};
}
componentWillMount() {
// Add a listener for the delta value change
this._val = { x:0, y:0 }
this.state.pan.addListener((value) => this._val = value);
// Initialize PanResponder with move handling
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (e, gesture) => true,
onPanResponderMove: Animated.event([
null, { dx: this.state.pan.x, dy: this.state.pan.y }
])
// adjusting delta value
this.state.pan.setValue({ x:0, y:0})
});
}
render() {
const panStyle = {
transform: this.state.pan.getTranslateTransform()
}
return (
<Animated.View
{...this.panResponder.panHandlers}
style={[panStyle, styles.circle]}
/>
);
}
}
let CIRCLE_RADIUS = 30;
let styles = StyleSheet.create({
circle: {
backgroundColor: "skyblue",
width: CIRCLE_RADIUS * 2,
height: CIRCLE_RADIUS * 2,
borderRadius: CIRCLE_RADIUS
}
});
this code the circle to the drag but I have drag another location it goes on the starting location so have to set the current location value so please tell me

React native svg heart size animation

I am trying to make a pulsing svg heart in ReactNative expo using an SVG image.
The only way I managed to make the heart to resize with an animated value is to change bind it to style: fontSize.
This seems to change size correctly, but the animation is really choppy.
Here is the code:
import React, { Component } from 'react';
import { Animated } from 'react-native';
import { SimpleLineIcons } from '#expo/vector-icons';
const AnimatedIcon = Animated.createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 24;
class GrowingHeart extends Component<any, any> {
size = new Animated.Value(TARGET_FONT_SIZE);
constructor(props) {
super(props);
Animated.sequence([
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
}),
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
})
]).start();
}
render() {
return (
<AnimatedIcon
style={{ fontSize: this.size }}
size={20}
name="heart"
color="red"
/>
);
}
}
I tried also to bind width and height but they are also choppy + they change container size, rather than the icon.
Is there a better way of doing this? Thanks
Seems that Animated api is simply still rubbish (please correct me if I am getting this wrong)
I re-wrote this with reanimated and it works smooth. The code below is not complete but shows how heart is growing with no choppiness, but rather perfectly smooth.
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';
import { SimpleLineIcons } from '#expo/vector-icons';
const {
createAnimatedComponent,
debug,
set,
get,
block,
eq,
Value,
cond,
greaterThan,
startClock,
timing,
Clock,
Code,
clockRunning,
stopClock
} = Animated;
const AnimatedIcon = createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 20;
class GrowingHeart extends Component<any, any> {
size = new Value(TARGET_FONT_SIZE);
clock = new Clock();
updatingValue = new Value(0);
clockState = {
finished: new Value(0),
position: new Value(5),
time: new Value(0),
frameTime: new Value(0)
};
clockConfig = {
duration: new Value(500),
toValue: new Value(GROWN_FONT_SIZE),
easing: Easing.linear
};
constructor(props) {
super(props);
}
render() {
const { color } = this.props;
const { updatingValue, size, clock, clockConfig, clockState } = this;
return (
<>
<Code>
{() =>
block([
cond(
// animation not triggered
eq(0, updatingValue),
[],
[
cond(
clockRunning(clock),
[],
[
set(clockState.finished, 0),
set(clockState.time, 0),
set(clockState.position, TARGET_FONT_SIZE),
set(clockState.frameTime, 0),
set(clockConfig.toValue, GROWN_FONT_SIZE),
startClock(clock)
]
),
cond(
greaterThan(0, updatingValue),
// is decreasing
[debug('going down', updatingValue)],
// is growing
[
timing(clock, clockState, clockConfig),
set(size, clockState.position)
]
),
cond(clockState.finished, [stopClock(clock)])
]
)
])
}
</Code>
<TouchableOpacity
onPress={() => {
this.updatingValue.setValue(1);
}}
>
<AnimatedIcon
style={{ fontSize: this.size }}
name="heart"
color={color}
/>
</TouchableOpacity>
</>
);
}
}

How can I pass properties to the parent constructor when using inheritance with React components?

I found myself making lots of components with functions to animate in and out in similar ways, so I thought perhaps I should do something like this to re-use code:
import ShrinkComponent from './shrink-component.js';
import { /* ... */ } from 'react-native';
class Input extends ShrinkComponent {
constructor(props) {
super(props);
this.properties = {
...props,
};
this.state = {
/* ... */
render() {
return (
<Animated.View style={{ ...this._style }}>
Where shrink-component.js looks like:
import React from 'react';
import { /* ... */ } from 'react-native';
class Blueprint extends React.Component {
constructor(height){
this._defaultHeight = height;
}
_style = {
opacity: new Animated.Value(1),
height: new Animated.Value(this._defaultHeight),
display: 'auto'
};
show(time = 250, callback) {
Animated.timing(
this._style.height,
{
toValue: this._defaultHeight,
duration: time
}
).start(()=> {
this._style.display = 'auto';
Animated.timing(
this._style.opacity,
{
toValue: 1,
duration: time
}
).start(()=>{
if (callback) callback();
});
});
}
hide(time = 250, callback) {
Animated.timing(
this._style.opacity,
{
toValue: 0,
duration: time
}
).start(()=> {
Animated.timing(
this._style.height,
{
toValue: 0,
duration: time
}
).start(()=> {
this._style.display = 'none';
if (callback) callback();
});
});
}
}
export default Blueprint;
But I ran into the issue of not actually knowing how to pass properties to the ShrinkComponent constructor, and that's ultimately my question here, but probably more importantly, before I implement this design, is this the wrong way to be doing what I'm trying to do? If so, what's the right way (I know that would normally be a broad question but I think within React, there is sort of a right and wrong way to do things)?
And if this is fine, how can I pass properties to the ShrinkComponent constructor when I create a new instance of Input?

React Native Pan Responder to Animate single dynamic view

I'm creating a series of cubes within a grid and I want to only move the cube that is pressed upon. Currently in my code it is moving all the cubes as one. I tried finding the individual key of the cube to try and target it for animation, but I'm not having any luck so far in figuring this out.
The myPanel component is just to layout the cubes that are defined in the renderCube function. It may be an issue with laying out all of the cubes with Animated.View, even though I added the key, but I can't access or figure out how to specifically animate only the selected cube.
import React, { Component } from 'react';
import { TouchableWithoutFeedback, View, ImageBackground, Dimensions, Animated, PanResponder } from 'react-native';
import myPanel from './myPanel';
const DATA = [
{ id: 1, color: '#ff8080' },
{ id: 2, color: '#80ff80' },
{ id: 3, color: '#ffff80' }
];
const winWidth = Dimensions.get('window').width;
const widthCalc = winWidth/400;
const cubeWidth = widthCalc*74;
const winHeight = Dimensions.get('window').height;
const heightCalc = ((winHeight-winWidth)/2)-5;
const xPush = 5*widthCalc;
const borRadius = 7.4*widthCalc;
let cnt = 0;
let xMove = 0;
let yMove = 0;
let currTarget = '033';
class Board extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY()
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onStartShouldSetPanResponder:(e, gestureState) => true,
onPanResponderGrant: (e, gestureState) => {
console.log(this.currTarget);
},
onPanResponderMove: Animated.event([
null,
{
dx: this.state.pan.x,
dy: this.state.pan.y,
},
]),
onPanResponderRelease: () => {
Animated.spring(
this.state.pan,
{toValue: {x: 0, y: 0}},
).start();
}
});
}
renderCube(item) {
return (
<Animated.View {...this._panResponder.panHandlers}
style={this.state.pan.getLayout()} key={item.id}>
<TouchableWithoutFeedback
onPressIn={()=>{this.currTarget = item.id; console.log('Set '+item.id);}}>
<View
style={{
backgroundColor:myColor,
width:cubeWidth,
height:cubeWidth,
position: 'absolute',
top: yMove,
left: xMove,
bottom: 0,
right: 0,
borderRadius:borRadius
}}
></View>
</TouchableWithoutFeedback>
</Animated.View>
);
}
render() {
return (
<myPanel
data={DATA}
renderCube={this.renderCube.bind(this)}
/>
);
}
}
export default Board;

How to remove network request failed error screen and display message "No internet connection" in react-native

How to remove network request failed error screen and display message "No internet connection" for better user experience in react-native when there is no internet connection.
You can use NetInfo in React-Native to check network state. This is link:
https://facebook.github.io/react-native/docs/netinfo.html
This is example code:
NetInfo.getConnectionInfo().then((connectionInfo) => {
if (connectionInfo.type === 'none') {
alert("No internet connection")
} else {
// online
// do something
}
});
Here i wrote an component for handling internet status issues refer this:
import React, { Component } from "react";
import {
View,
NetInfo,
Animated,
Easing,
Dimensions,
Platform,
AppState
} from "react-native";
// import { colors, Typography, primaryFont } from "../../Config/StylesConfig";
import { connect } from "react-redux";
import { changeConnectionStatus } from "../../actions/authActions";
import CustomText from "./CustomText";
import firebaseHelper from "../../helpers/firebaseHelper";
const { width } = Dimensions.get("window");
class InternetStatusBar extends Component {
constructor(props) {
super(props);
this.state = {
isNetworkConnected: true
};
this._updateConnectionStatus = this._updateConnectionStatus.bind(this);
this.positionValue = new Animated.Value(-26);
this.colorValue = new Animated.Value(0);
this.isMount = true;
this.isOnline = true;
}
_handleAppStateChange = nextAppState => {
if (nextAppState.match(/inactive|background/) && this.isOnline) {
firebaseHelper.goOffline();
// console.log("offline");
this.isOnline = false;
} else if (nextAppState === "active" && this.isOnline === false) {
firebaseHelper.goOnline();
// console.log("online");
this.isOnline = true;
}
};
componentDidMount() {
AppState.addEventListener("change", this._handleAppStateChange);
// NetInfo.isConnected.fetch().done(isNetworkConnected => {
// this._updateConnectionStatus(isNetworkConnected);
// });
NetInfo.isConnected.addEventListener(
"connectionChange",
this._updateConnectionStatus
);
}
componentWillUnmount() {
AppState.removeEventListener("change", this._handleAppStateChange);
NetInfo.isConnected.removeEventListener(
"connectionChange",
this._updateConnectionStatus
);
}
_updateConnectionStatus(isNetworkConnected) {
// this.setState({ isNetworkConnected });
if (this.isMount) {
this.isMount = false;
} else {
if (isNetworkConnected) {
this.animateColorChange(isNetworkConnected);
setTimeout(() => {
this.animateErrorView(isNetworkConnected);
}, 1000);
} else {
this.animateErrorView(isNetworkConnected);
this.colorValue = new Animated.Value(0);
}
}
// this.props.changeConnectionStatus(isNetworkConnected);
}
// componentWillReceiveProps = nextProps => {
// if (
// nextProps.isInternetConnected &&
// nextProps.isInternetConnected != this.state.isInternetConnected
// ) {
// const date = new Date();
// Actions.refresh({ refreshContent: date.getTime() });
// }
// };
animateErrorView(connected) {
Animated.timing(this.positionValue, {
toValue: connected ? -40 : Platform.OS === "ios" ? 20 : 0,
easing: Easing.linear,
duration: 600
}).start();
}
animateColorChange(connected) {
Animated.timing(this.colorValue, {
toValue: connected ? 150 : 0,
duration: 800
}).start();
}
render() {
return (
<Animated.View
style={[
{
position: "absolute",
backgroundColor: this.colorValue.interpolate({
inputRange: [0, 150],
outputRange: ["rgba(0,0,0,0.6)", "rgba(75, 181, 67, 0.8)"]
}),
zIndex: 1,
width: width,
top: 0,
transform: [{ translateY: this.positionValue }]
}
]}
>
<View
style={[
{
padding: 4,
flexDirection: "row",
flex: 1
}
]}
>
<CustomText
style={{
fontSize: 12,
textAlign: "center",
flex: 1
}}
>
{this.state.isInternetConnected ? "Back online" : "No connection"}
</CustomText>
</View>
</Animated.View>
);
}
}
const mapStateToProps = state => {
return {
isInternetConnected: state.user.isInternetConnected
};
};
export default connect(mapStateToProps, { changeConnectionStatus })(
InternetStatusBar
);
A Snackbar is a good way to convey this. Have a look at this library :
https://github.com/9gag-open-source/react-native-snackbar-dialog
Easy and Simple with good user experience
use "react-native-offline-status"
Reference:
https://github.com/rgabs/react-native-offline-status