I'm trying to update an old code. Something is going wrong in the code I shared, and I can't figure it out because I'm not fully proficient in the reanimated library. If anyone can help, I would appreciate it. I'm creating a voting screen, and based on the touched stars on this screen, I will change the visible facial expression in the photo I share.
RatingScreen.js
import React from 'react';
import { TouchableOpacity, StyleSheet, Text, View, Dimensions } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import Animated, { Easing, useSharedValue, withSpring, useAnimatedStyle } from 'react-native-reanimated';
import * as flubber from 'flubber';
import Svg, { G, Path } from 'react-native-svg';
import Icon1 from 'react-native-vector-icons/Feather';
const { width, height } = Dimensions.get('screen');
const fill = "#333";
const types = ['upset', 'sad', 'neutral', 'smile', 'excited'];
const PATHS = {
"upset": "M141.5 132.55C140.92 75.87 120.92 48.22 81.5 49.63C42.09 51.03 22.09 78.67 21.5 132.55L141.5 132.55Z",
"sad": "M122.32 87.65C121.94 68.08 108.83 58.53 83 59.02C57.17 59.5 44.06 69.04 43.68 87.65L122.32 87.65Z",
"neutral": "M38.02 58.05L99.77 40.83L102.99 52.35L41.23 69.57L38.02 58.05Z",
"smile": "M122.32 64.68C121.94 84.25 108.83 93.79 83 93.31C57.17 92.82 44.06 83.28 43.68 64.68L122.32 64.68Z",
"excited": "M142.99 49.74C142.4 106.42 122.4 134.06 82.99 132.66C43.57 131.26 23.57 103.62 22.99 49.74L142.99 49.74Z",
"left-eye": "M30.43 16.78C30.43 24.39 24.29 30.57 16.72 30.57C9.15 30.57 3 24.39 3 16.78C3 9.18 9.15 3 16.72 3C24.29 3 30.43 9.18 30.43 16.78Z",
"right-eye": "M162.99 16.79C162.99 24.4 156.84 30.57 149.27 30.57C141.7 30.57 135.56 24.4 135.56 16.79C135.56 9.18 141.7 3.01 149.27 3.01C156.84 3.01 162.99 9.18 162.99 16.79Z"
};
const GRADIENTS = {
"upset": ["rgb(231, 97, 97)", "rgb(236, 49, 49)"],
"sad": ["rgb(247,152,48)", "rgb(231, 97, 97)"],
"neutral": ["rgb(243, 189, 67)", "rgb(203,96,32)"],
"smile": ["rgb(238,172,77)", "rgb(187, 230, 95)"],
"excited": ["rgb(95,230,118)", "rgb(46, 232, 78)"],
};
class RatingScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
path: PATHS.neutral,
background: GRADIENTS.neutral,
type: "neutral",
index: -1,
progress: new Animated.Value(0),
};
}
interpolatePaths = (type, index) => {
const interpolator = flubber.interpolate(this.state.path, PATHS[type], { maxSegmentLength: 2 });
Animated.timing(this.state.progress, {
toValue: 1,
duration: 400,
easing: Easing.inOut(Easing.ease),
useNativeDriver: false,
}).start(() => {
this.setState({
path: interpolator(1),
background: GRADIENTS[type],
type,
index,
});
this.state.progress.setValue(0);
});
};
render() {
const animatedStyle = {
d: this.state.path,
background: this.state.background,
};
return (
<LinearGradient colors={this.state.background} style={styles.gradient}>
<View style={styles.headings}>
<Text style={styles.heading}>Please rate your feedback</Text>
</View>
<View style={styles.svgWrapper}>
<Svg width={width} height={height / 3} viewBox="0 0 166 136" style={styles.svgContainer}>
<G>
<Path d={PATHS["left-eye"]} fill={fill} />
<Animated.View style={animatedStyle}>
<Path d={PATHS["right-eye"]} fill={fill} />
</Animated.View>
</G>
</Svg>
<View style={styles.feedbackWrapper}>
{types.map((type, index) => (
<TouchableOpacity key={type} onPress={() => this.interpolatePaths(type, index)}>
<Icon1 name={this.state.index >= index ? "star" : "star"} size={32} color="#fff" />
</TouchableOpacity>
))}
</View>
</View>
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
gradient: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
feedbackWrapper: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
height: 60,
borderRadius: 30,
backgroundColor: "rgba(0, 0, 0, 0.05)",
width: width * .9
},
headings: {
flex: .3,
justifyContent: "center"
},
heading: {
color: "#fff",
fontSize: 42,
lineHeight: 42,
fontWeight: '700'
},
body: {
color: "#fff",
fontFamily: "Menlo"
},
svgContainer: {
marginBottom: 40
},
svgWrapper: {
flex: .6,
alignItems: "center",
justifyContent: "center"
}
});
export default RatingScreen;
old code
import React from 'react';
import { TouchableOpacity, StyleSheet, Text, View, Dimensions } from 'react-native';
import Gradient from 'react-native-css-gradient';
import { interpolate } from 'flubber';
import { tween, easing } from 'popmotion';
import { Svg } from 'expo'
import { AntDesign } from '@expo/vector-icons';
const { width, height } = Dimensions.get('screen');
const { Path, G } = Svg;
const fill = "#333";
const types = ['upset', 'sad', 'neutral', 'smile', 'excited'];
const PATHS = {
"upset": "M141.5 132.55C140.92 75.87 120.92 48.22 81.5 49.63C42.09 51.03 22.09 78.67 21.5 132.55L141.5 132.55Z",
"sad": "M122.32 87.65C121.94 68.08 108.83 58.53 83 59.02C57.17 59.5 44.06 69.04 43.68 87.65L122.32 87.65Z",
"neutral": "M38.02 58.05L99.77 40.83L102.99 52.35L41.23 69.57L38.02 58.05Z",
"smile": "M122.32 64.68C121.94 84.25 108.83 93.79 83 93.31C57.17 92.82 44.06 83.28 43.68 64.68L122.32 64.68Z",
"excited": "M142.99 49.74C142.4 106.42 122.4 134.06 82.99 132.66C43.57 131.26 23.57 103.62 22.99 49.74L142.99 49.74Z",
"left-eye": "M30.43 16.78C30.43 24.39 24.29 30.57 16.72 30.57C9.15 30.57 3 24.39 3 16.78C3 9.18 9.15 3 16.72 3C24.29 3 30.43 9.18 30.43 16.78Z",
"right-eye": "M162.99 16.79C162.99 24.4 156.84 30.57 149.27 30.57C141.7 30.57 135.56 24.4 135.56 16.79C135.56 9.18 141.7 3.01 149.27 3.01C156.84 3.01 162.99 9.18 162.99 16.79Z"
};
const GRADIENTS = {
"upset": "linear-gradient(to bottom, rgb(231, 97, 97), rgb(236, 49, 49))",
"sad": "linear-gradient(to bottom, rgb(247,152,48), rgb(231, 97, 97))",
"neutral": "linear-gradient(to bottom, rgb(243, 189, 67), rgb(203,96,32))",
"smile": "linear-gradient(to bottom, rgb(238,172,77), rgb(187, 230, 95))",
"excited": "linear-gradient(to bottom, rgb(95,230,118), rgb(46, 232, 78))",
};
export default class App extends React.Component {
state = {
path: PATHS.neutral,
background: GRADIENTS.neutral,
type: "neutral",
index: -1
}
interpolatePaths = (type, index) => {
const interpolator = interpolate(this.state.path, PATHS[type], { maxSegmentLength: 2 });
tween({
duration: 400,
ease: easing.easeInOut,
from: { i: 0, background: this.state.background },
to: { i: 1, background: GRADIENTS[type] }
})
.pipe(({ i, background }) => ({ path: interpolator(i), background }))
.start(({ path, background }) => {
this.setState({
path, background, type, index
})
})
}
render() {
return (
<Gradient gradient={this.state.background} style={styles.gradient}>
<View style={styles.headings}>
<Text style={styles.heading}>Please rate your feedback</Text>
<Text style={styles.body}>Do let us know your thoughts.</Text>
<Text style={styles.body}>Your feedback matters!</Text>
</View>
<View style={styles.svgWrapper}>
<Svg width={width} height={height / 3} viewBox="0 0 166 136" style={styles.svgContainer}>
<G>
<Path d={PATHS["left-eye"]} fill={fill} />
<Path d={this.state.path} fill={fill} />
<Path d={PATHS["right-eye"]} fill={fill} />
</G>
</Svg>
<View style={styles.feedbackWrapper}>
{types.map((type, index) => (
<TouchableOpacity key={type} onPress={() => this.interpolatePaths(type, index)}>
<AntDesign name={this.state.index >= index ? "star" : "staro"} size={32} color="#fff" />
</TouchableOpacity>
))}
</View>
</View>
</Gradient>
);
}
}
const styles = StyleSheet.create({
gradient: {
width,
height,
alignItems: "center",
},
feedbackWrapper: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
height: 60,
borderRadius: 30,
backgroundColor: "rgba(0, 0, 0, 0.05)",
width: width * .9
},
headings: {
flex: .4,
justifyContent: "center"
},
heading: {
color: "#fff",
fontSize: 42,
lineHeight: 42,
fontWeight: '700'
},
body: {
color: "#fff",
fontFamily: "Menlo"
},
svgContainer: {
marginBottom: 40
},
svgWrapper: {
flex: .6,
alignItems: "center",
justifyContent: "center"
}
});
I tried old libraries but it didn't work
I tried like this but it didn't work and i don't know how can i use popmotion
import React from 'react';
import { TouchableOpacity, StyleSheet, Text, View, Dimensions } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import * as flubber from 'flubber';
import { animate, easeInOut } from 'popmotion';
import Svg, { G, Path } from 'react-native-svg';
import Icon1 from 'react-native-vector-icons/Feather';
const { width, height } = Dimensions.get('screen');
const fill = "#333";
const types = ['upset', 'sad', 'neutral', 'smile', 'excited'];
const PATHS = {
"upset": "M141.5 132.55C140.92 75.87 120.92 48.22 81.5 49.63C42.09 51.03 22.09 78.67 21.5 132.55L141.5 132.55Z",
"sad": "M122.32 87.65C121.94 68.08 108.83 58.53 83 59.02C57.17 59.5 44.06 69.04 43.68 87.65L122.32 87.65Z",
"neutral": "M38.02 58.05L99.77 40.83L102.99 52.35L41.23 69.57L38.02 58.05Z",
"smile": "M122.32 64.68C121.94 84.25 108.83 93.79 83 93.31C57.17 92.82 44.06 83.28 43.68 64.68L122.32 64.68Z",
"excited": "M142.99 49.74C142.4 106.42 122.4 134.06 82.99 132.66C43.57 131.26 23.57 103.62 22.99 49.74L142.99 49.74Z",
"left-eye": "M30.43 16.78C30.43 24.39 24.29 30.57 16.72 30.57C9.15 30.57 3 24.39 3 16.78C3 9.18 9.15 3 16.72 3C24.29 3 30.43 9.18 30.43 16.78Z",
"right-eye": "M162.99 16.79C162.99 24.4 156.84 30.57 149.27 30.57C141.7 30.57 135.56 24.4 135.56 16.79C135.56 9.18 141.7 3.01 149.27 3.01C156.84 3.01 162.99 9.18 162.99 16.79Z"
};
const GRADIENTS = {
"upset": ["rgb(231, 97, 97)", "rgb(236, 49, 49)"],
"sad": ["rgb(247,152,48)", "rgb(231, 97, 97)"],
"neutral": ["rgb(243, 189, 67)", "rgb(203,96,32)"],
"smile": ["rgb(238,172,77)", "rgb(187, 230, 95)"],
"excited": ["rgb(95,230,118)", "rgb(46, 232, 78)"],
};
export default class RatingScreen extends React.Component {
state = {
path: PATHS.neutral,
background: GRADIENTS.neutral,
type: "neutral",
index: -1
}
interpolatePaths = (type, index) => {
const interpolator = flubber.interpolate(this.state.path, PATHS[type], { maxSegmentLength: 2 });
animate({
duration: 400,
ease: easeInOut,
from: { i: 0, background: this.state.background },
to: { i: 1, background: GRADIENTS[type] }
})
.pipe(({ i, background }) => ({ path: interpolator(i), background }))
.start(({ path, background }) => {
this.setState({
path, background, type, index
})
})
}
render() {
return (
<LinearGradient colors={this.state.background} style={styles.gradient}>
<View style={styles.headings}>
<Text style={styles.heading}>Please rate your feedback</Text>
</View>
<View style={styles.svgWrapper}>
<Svg width={width} height={height / 3} viewBox="0 0 166 136" style={styles.svgContainer}>
<G>
<Path d={PATHS["left-eye"]} fill={fill} />
<Path d={this.state.path} fill={fill} />
<Path d={PATHS["right-eye"]} fill={fill} />
</G>
</Svg>
<View style={styles.feedbackWrapper}>
{types.map((type, index) => (
<TouchableOpacity key={type} onPress={() => this.interpolatePaths(type, index)}>
<Icon1 name={this.state.index >= index ? "star" : "star"} size={32} color="#fff" />
</TouchableOpacity>
))}
</View>
</View>
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
gradient: {
width,
height,
alignItems: "center",
},
feedbackWrapper: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
height: 60,
borderRadius: 30,
backgroundColor: "rgba(0, 0, 0, 0.05)",
width: width * .9
},
headings: {
flex: .3,
justifyContent: "center"
},
heading: {
color: "#fff",
fontSize: 42,
lineHeight: 42,
fontWeight: '700'
},
body: {
color: "#fff",
fontFamily: "Menlo"
},
svgContainer: {
marginBottom: 40
},
svgWrapper: {
flex: .6,
alignItems: "center",
justifyContent: "center"
}
});
Due to issues with the libraries I was using, I switched to different libraries and abandoned changing background colors with linear. By utilizing some simple Reanimated functions, I achieved success with a straightforward code.
import React from 'react';
import { TouchableOpacity, StyleSheet, View, Dimensions, Text, TouchableWithoutFeedback } from 'react-native';
import Svg, { G, Path } from 'react-native-svg';
import Icon from 'react-native-vector-icons/AntDesign';
import Animated, { useSharedValue, useAnimatedProps, withTiming, Easing } from 'react-native-reanimated';
import { parse, interpolatePath } from 'react-native-redash';
import LinearGradient from 'react-native-linear-gradient';
const { width, height } = Dimensions.get('screen');
const fill = "white";
const lefteye = "M30.43 16.78C30.43 24.39 24.29 30.57 16.72 30.57C9.15 30.57 3 24.39 3 16.78C3 9.18 9.15 3 16.72 3C24.29 3 30.43 9.18 30.43 16.78Z";
const righteye = "M162.99 16.79C162.99 24.4 156.84 30.57 149.27 30.57C141.7 30.57 135.56 24.4 135.56 16.79C135.56 9.18 141.7 3.01 149.27 3.01C156.84 3.01 162.99 9.18 162.99 16.79Z";
const App = ({ }) => {
const AnimatedPath = Animated.createAnimatedComponent(Path);
const progress = useSharedValue(0);
const [rating, setRating] = React.useState(0);
const expressions = [
parse("M141.5 132.55C140.92 75.87 120.92 48.22 81.5 49.63C42.09 51.03 22.09 78.67 21.5 132.55L141.5 132.55Z"),
parse("M122.32 87.65C121.94 68.08 108.83 58.53 83 59.02C57.17 59.5 44.06 69.04 43.68 87.65L122.32 87.65Z"),
parse("M38.02 58.05L99.77 40.83L102.99 52.35L41.23 69.57L38.02 58.05Z"),
parse("M122.32 64.68C121.94 84.25 108.83 93.79 83 93.31C57.17 92.82 44.06 83.28 43.68 64.68L122.32 64.68Z"),
parse("M142.99 49.74C142.4 106.42 122.4 134.06 82.99 132.66C43.57 131.26 23.57 103.62 22.99 49.74L142.99 49.74Z"),
];
const animatedProps = useAnimatedProps(() => {
return {
d: interpolatePath(progress.value, [0, 1, 2, 3, 4], expressions),
};
});
const handleStarPress = (value) => {
setRating(value);
progress.value = withTiming(value, { duration: 530, easing: Easing.bezier(0.33, 1, 0.68, 1) });
};
const renderStar = (value) => (
<TouchableOpacity onPress={() => handleStarPress(value)}>
<Icon name={rating >= value ? "star" : "staro"} size={32} color={fill} />
</TouchableOpacity>
);
return (
<LinearGradient style={styles.container} colors={['#1d4c1e', '#151618']}>
<View style={styles.svgWrapper}>
<View style={{ width: width, alignItems: 'center' }}>
<Svg width={width} height={height / 4} viewBox="0 0 166 136" style={styles.svgContainer}>
<G>
<Path d={lefteye} fill={fill} />
<AnimatedPath animatedProps={animatedProps} fill={fill} />
<Path d={righteye} fill={fill} />
</G>
</Svg>
</View>
</View>
<View style={styles.feedbackWrapper}>
{renderStar(0)}
{renderStar(1)}
{renderStar(2)}
{renderStar(3)}
{renderStar(4)}
</View>
</LinearGradient>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: 'center',
paddingBottom: 40,
backgroundColor: 'white'
},
feedbackWrapper: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
width: width * .9,
position: 'absolute',
bottom: 50,
},
headings: {
justifyContent: "center"
},
heading: {
color: "white",
lineHeight: 20,
fontFamily: "Poppins-SemiBold",
fontSize: 16,
},
svgContainer: {
marginBottom: 40
},
svgWrapper: {
alignItems: "center",
justifyContent: "center"
},
});
export default App;