我遇到动画问题。我试图用两种不同的视图翻转卡片。当用户在两张不同的卡片之间滚动时,我也试图创建滚动效果。当代码以下面的方式组合时,它会创建一个我无法压缩的错误。我添加了一个图像,以直观地表示我的问题。
我感谢任何帮助。
我的生命周期方法:
componentWillMount() {
this.animatedValue = new Animated.Value(0);
this.value = 0;
this.animatedValue.addListener(({ value }) => {
this.value = value;
this.setState({ value });
});
this.frontInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg']
});
this.backInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
});
}
}
此动画用于制作翻转动画:
flipCard() {
if (this.value >= 90) {
this.setState({
isWaiting: true
});
Animated.spring(this.animatedValue, {
toValue: 0,
friction: 8,
tension: 10
}).start(() => {
this.setState({
isWaiting: false
});
});
} else {
this.setState({
isWaiting: true
});
Animated.spring(this.animatedValue, {
toValue: 180,
friction: 8,
tension: 10
}).start(() => {
this.setState({ isWaiting: false });
});
}
}
这是通过flipCard功能翻转的View。如果您在其中一个视图中看到,则有一个名为transitionAnimation的函数。这用于产生滚动效果。
<View style={styles.scrollPage}>
<View>
<Animated.View
style={[
frontAnimatedStyle,
styles.screen,
this.transitionAnimation(index)
]}
>
<Text style={styles.text}>{question.question}</Text>
</Animated.View>
<Animated.View
style={[
styles.screen,
backAnimatedStyle,
styles.back,
this.transitionAnimation(index)
]}
>
<Text style={styles.text}>{question.answer}</Text>
</Animated.View>
transitionAnimation:
transitionAnimation = index => {
if (!this.state.isWaiting) {
return {
transform: [
{ perspective: 800 },
{
scale: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: [0.25, 1, 0.25]
})
},
{
rotateX: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: ['45deg', '0deg', '45deg']
})
},
{
rotateY: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: ['-45deg', '0deg', '45deg']
})
}
]
};
}
};
我的渲染功能:
render() {
const { flashcards } = this.state;
return (
<View style={styles.container}>
<View
style={{
alignItems: 'flex-end',
marginTop: 10
}}
>
<Progress.Circle
size={70}
showsText
progress={this.state.timer}
formatText={text => {
return (this.state.timer * 100).toFixed(0);
}}
/>
</View>
<Animated.ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: xOffset } } }],
{ useNativeDriver: true }
)}
horizontal
pagingEnabled
style={styles.scrollView}
>
{this.state.flashcards && this.renderCard()}
</Animated.ScrollView>
</View>
);
}
}
我还创建了一个小吃店,您可以在那里查看问题。 https://snack.expo.io/@louis345/flaschards
答案 0 :(得分:8)
你有很多问题:
主要问题是因为您没有正确存储每张卡的状态(如果是否翻转)。例如,您可以将flippedCards
数组或设置添加到您的状态,并在每次翻转卡片时更新它,以便在动画结束时调用setState
后正确呈现,并正确呈现其他卡片那些没有翻过来。
您一次渲染和动画(翻转和转换)所有卡片,但您应该只渲染三张卡片(当前和邻居),并且您应该只翻转当前卡片。
性能问题:您在每个渲染上创建过渡样式和其他函数,这会使渲染速度变慢。
应重构的其他代码。
我解决了1和3个问题,并重构了一下。 2取决于你:
import React, { Component } from 'react';
import { Animated, Dimensions, StyleSheet, Text, View, TouchableOpacity, TouchableWithoutFeedback } from 'react-native';
import { EvilIcons, MaterialIcons } from '@expo/vector-icons';
const SCREEN_WIDTH = Dimensions.get('window').width;
export default class App extends Component {
constructor(props) {
super(props);
const flashcards = ['konichiwa','hi','genki desu','how are you'];
this.state = {
flashcards,
flipped: flashcards.map(() => false),
flipping: false
};
this.flipValue = new Animated.Value(0);
this.frontAnimatedStyle = {
transform: [{
rotateY: this.flipValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '180deg']
})
}]
};
this.backAnimatedStyle = {
transform: [{
rotateY: this.flipValue.interpolate({
inputRange: [0, 1],
outputRange: ['180deg', '360deg']
})
}]
};
let xOffset = new Animated.Value(0);
this.onScroll = Animated.event(
[{ nativeEvent: { contentOffset: { x: xOffset } } }],
{ useNativeDriver: false }
);
this.transitionAnimations = this.state.flashcards.map((card, index) => ({
transform: [
{ perspective: 800 },
{
scale: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: [0.25, 1, 0.25]
})
},
{
rotateX: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: ['45deg', '0deg', '45deg']
})
},
{
rotateY: xOffset.interpolate({
inputRange: [
(index - 1) * SCREEN_WIDTH,
index * SCREEN_WIDTH,
(index + 1) * SCREEN_WIDTH
],
outputRange: ['-45deg', '0deg', '45deg']
})
}
]
}));
}
render() {
return (
<View style={styles.container}>
<Animated.ScrollView
scrollEnabled={!this.state.flipping}
scrollEventThrottle={16}
onScroll={this.onScroll}
horizontal
pagingEnabled
style={styles.scrollView}>
{this.state.flashcards.map(this.renderCard)}
</Animated.ScrollView>
</View>
);
}
renderCard = (question, index) => {
const isFlipped = this.state.flipped[index];
return (
<TouchableWithoutFeedback key={index} onPress={() => this.flipCard(index)}>
<View>
<View style={styles.scrollPage}>
<View>
{(this.state.flipping || !isFlipped) && <Animated.View
style={[
this.state.flipping ? this.frontAnimatedStyle : this.transitionAnimations[index],
styles.screen
]}
>
<Text style={styles.text}>{this.state.flashcards[index]}</Text>
</Animated.View>}
{(this.state.flipping || isFlipped) && <Animated.View
style={[
styles.screen,
this.state.flipping ? this.backAnimatedStyle : this.transitionAnimations[index],
this.state.flipping && styles.back
]}
>
<Text style={styles.text}>{this.state.flashcards[index+1]}</Text>
</Animated.View>}
</View>
</View>
<View style={styles.iconStyle}>
<TouchableOpacity>
<EvilIcons name="check" size={80} color={'#5CAF25'} />
</TouchableOpacity>
<TouchableOpacity>
<MaterialIcons name="cancel" size={70} color={'#b71621'} />
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
);
}
flipCard = index => {
if (this.state.flipping) return;
let isFlipped = this.state.flipped[index];
let flipped = [...this.state.flipped];
flipped[index] = !isFlipped;
this.setState({
flipping: true,
flipped
});
this.flipValue.setValue(isFlipped ? 1: 0);
Animated.spring(this.flipValue, {
toValue: isFlipped ? 0 : 1,
friction: 8,
tension: 10
}).start(() => {
this.setState({ flipping: false });
});
}
}
const styles = StyleSheet.create({
container: {
backgroundColor:'red',
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between'
},
scrollView: {
flexDirection: 'row',
backgroundColor: 'black'
},
scrollPage: {
width: SCREEN_WIDTH,
padding: 20
},
screen: {
height: 400,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 25,
backgroundColor: 'white',
width: SCREEN_WIDTH - 20 * 2,
backfaceVisibility: 'hidden'
},
text: {
fontSize: 45,
fontWeight: 'bold'
},
iconStyle: {
flexDirection: 'row',
justifyContent: 'center'
},
back: {
position: 'absolute',
top: 0,
backfaceVisibility: 'hidden'
}
});
至少现在它运作正常。