Commit 802b6795 authored by Martin Santangelo's avatar Martin Santangelo

(feat) improve pulse animation with reanimated and fix ios simulator issue

1 merge request!434Feat improve pulse animation with reanimated and fix ios simulator issue
......@@ -9,6 +9,7 @@ import {
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock'));
// mock backhandler
......
import React from 'react';
import { View, StyleSheet, Animated, Easing, Dimensions } from 'react-native';
const { height, width } = Dimensions.get('window');
export default class Pulse extends React.Component {
constructor(props) {
super(props);
this.anim = new Animated.Value(0);
}
componentDidMount() {
Animated.timing(this.anim, {
toValue: 1,
duration: this.props.interval,
easing: Easing.in,
})
.start();
}
render() {
const { size, pulseMaxSize, borderColor, backgroundColor, getStyle } = this.props;
return (
<View style={[styles.circleWrapper, {
width: pulseMaxSize,
height: pulseMaxSize,
marginLeft: -pulseMaxSize/2,
marginTop: -pulseMaxSize/2,
}]}>
<Animated.View
style={[styles.circle, {
borderColor,
backgroundColor,
width: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [size, pulseMaxSize]
}),
height: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [size, pulseMaxSize]
}),
borderRadius: pulseMaxSize/2,
opacity: this.anim.interpolate({
inputRange: [0, 1],
outputRange: [1, 0]
})
}, getStyle && getStyle(this.anim)]}
/>
</View>
);
}
import { View, StyleSheet } from 'react-native';
import Animated, { Easing } from "react-native-reanimated";
import { bInterpolate, loop } from "react-native-redash";
const { Value, useCode, set } = Animated;
export default function(props) {
const animation = new Value(0);
useCode(
set(
animation,
loop({
toValue: 1,
duration: 1000,
easing: Easing.in(Easing.ease),
}),
),
[animation],
);
const scale = bInterpolate(animation, 1, 1.3);
const opacity = bInterpolate(animation, 1, 0);
const pulseMaxSize = Math.round(1.3 * props.size);
return (
<View
style={[
styles.circleWrapper,
{
width: pulseMaxSize,
height: pulseMaxSize,
marginLeft: -pulseMaxSize/2,
marginTop: -pulseMaxSize/2,
}
]}
>
<Animated.View
style={{
transform: [{scale}],
backgroundColor: 'red',
borderRadius: props.size / 2,
width: props.size,
height: props.size,
opacity,
}}
/>
</View>
);
}
const styles = StyleSheet.create({
circleWrapper: {
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
// left: width/8,
// top: height/2,
},
circle: {
borderWidth: 4 * StyleSheet.hairlineWidth,
},
circleWrapper: {
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
// left: width/8,
// top: height/2,
},
circle: {
borderWidth: 4 * StyleSheet.hairlineWidth,
},
});
\ No newline at end of file
import React from 'react';
import { View, Image, TouchableOpacity, Animated, Easing } from 'react-native';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import Pulse from './Pulse';
import FastImage from 'react-native-fast-image';
/**
* Based on https://github.com/wissile/react-native-pulse-anim
* Pulse avatar
*/
export default class PulseAnimAvatar extends React.Component {
constructor(props) {
super(props);
state = {};
this.state = {
circles: []
};
/**
* Derive state from props
* @param {object} nextProps
* @param {object} prevState
*/
static getDerivedStateFromProps(nextProps, prevState) {
if (
nextProps.size !== prevState.size ||
nextProps.avatar !== prevState.avatar
) {
return {
sizeStyle: {
width: nextProps.size,
height: nextProps.size,
},
imageStyle: {
width: nextProps.size,
height: nextProps.size,
borderRadius: nextProps.size / 2,
backgroundColor: nextProps.avatarBackgroundColor
},
avatarUri: {
uri: nextProps.avatar
},
};
}
return null;
}
this.counter = 1;
this.setInterval = null;
this.anim = new Animated.Value(1);
}
/**
* Render
*/
render() {
const {onPress} = this.props;
componentDidMount() {
this.setCircleInterval();
}
componentWillUnmount() {
clearInterval(this.setInterval);
}
setCircleInterval() {
this.setInterval = setInterval(this.addCircle.bind(this), this.props.interval);
this.addCircle();
}
addCircle() {
this.setState({ circles: [...this.state.circles, this.counter] });
this.counter++;
}
onPressIn() {
Animated.timing(this.anim, {
toValue: this.props.pressInValue,
duration: this.props.pressDuration,
easing: this.props.pressInEasing,
}).start(() => clearInterval(this.setInterval));
}
onPressOut() {
Animated.timing(this.anim, {
toValue: 1,
duration: this.props.pressDuration,
easing: this.props.pressOutEasing,
}).start(this.setCircleInterval.bind(this));
}
render() {
const { size, avatar, avatarBackgroundColor, interval, onPress } = this.props;
return (
<View style={{
// flex: 1,
backgroundColor: 'transparent',
// width: size,
// height: size,
justifyContent: 'center',
alignItems: 'center',
}}>
{this.state.circles.map((circle) => (
<Pulse
key={circle}
{...this.props}
/>
))}
<TouchableOpacity
activeOpacity={.5}
// onPressIn={this.onPressIn.bind(this)}
// onPressOut={this.onPressOut.bind(this)}
return (
<View style={styles.main}>
<Pulse
{...this.props}
/>
<TouchableOpacity
activeOpacity={.5}
onPress={onPress}
style={{
width: size,
height: size,
}}
>
<FastImage
source={{ uri: avatar }}
style={{
width: size,
height: size,
borderRadius: size/2,
backgroundColor: avatarBackgroundColor,
...this.props.style
}}
/>
</TouchableOpacity>
</View>
);
}
style={this.state.sizeStyle}
>
<FastImage
source={this.state.avatarUri}
style={[this.state.imageStyle, this.props.style]}
/>
</TouchableOpacity>
</View>
);
}
}
PulseAnimAvatar.defaultProps = {
interval: 2000,
size: 100,
pulseMaxSize: 200,
avatar: undefined,
avatarBackgroundColor: 'transparent',
pressInValue: 0.8,
pressDuration: 150,
pressInEasing: Easing.in,
pressOutEasing: Easing.in,
borderColor: '#1c1d1f',
backgroundColor: '#D1D1D1',
getStyle: undefined,
};
\ No newline at end of file
style: null,
};
const styles = StyleSheet.create({
main: {
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
},
});
......@@ -1493,6 +1493,11 @@ abort-controller@^3.0.0:
dependencies:
event-target-shim "^5.0.0"
abs-svg-path@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf"
integrity sha1-32Acjo0roQ1KdtYl4japo5wnI78=
absolute-path@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7"
......@@ -7423,6 +7428,13 @@ normalize-path@^2.1.1:
dependencies:
remove-trailing-separator "^1.0.1"
normalize-svg-path@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-1.0.1.tgz#6f729ad6b70bb4ca4eff2fe4b107489efe1d56fe"
integrity sha1-b3Ka1rcLtMpO/y/ksQdInv4dVv4=
dependencies:
svg-arc-to-cubic-bezier "^3.0.0"
normalize-url@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
......@@ -7969,6 +7981,11 @@ parse-path@^4.0.0:
is-ssh "^1.3.0"
protocols "^1.4.0"
parse-svg-path@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb"
integrity sha1-en7A0esG+lMlx9PgCbhZoJtdSes=
parse-url@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.1.tgz#99c4084fc11be14141efa41b3d117a96fcb9527f"
......@@ -8790,6 +8807,16 @@ react-native-reanimated@^1.3.0:
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.4.0.tgz#7f1acbf9be08492d834f512700570978052be2f9"
integrity sha512-tO7nSNNP+iRLVbkcSS5GXyDBb7tSI02+XuRL3/S39EAr35rnvUy2JfeLUQG+fWSObJjnMVhasUDEUwlENk8IXw==
react-native-redash@^8.6.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/react-native-redash/-/react-native-redash-8.6.0.tgz#a99f1e714f076816b17f44cbe77661a915079feb"
integrity sha512-Hjaz+rcyK/8w84/cPgJVajcE/86y8njw/OVp74qXRnnzhEo+n4O5UGcmP+WyVxCZ2mtgfYJevoqGP5GczEzM8Q==
dependencies:
abs-svg-path "^0.1.1"
normalize-svg-path "^1.0.1"
parse-svg-path "^0.1.2"
use-memo-one "^1.1.1"
react-native-safe-area-view@^0.14.1, react-native-safe-area-view@^0.14.6:
version "0.14.8"
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.8.tgz#ef33c46ff8164ae77acad48c3039ec9c34873e5b"
......@@ -10380,6 +10407,11 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
svg-arc-to-cubic-bezier@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz#390c450035ae1c4a0104d90650304c3bc814abe6"
integrity sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==
swarm-js@0.1.39:
version "0.1.39"
resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8"
......@@ -10935,6 +10967,11 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
use-memo-one@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
......
Please register or to comment