Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/react-native/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使用React本机动画存在渲染问题_Javascript_React Native - Fatal编程技术网

Javascript 使用React本机动画存在渲染问题

Javascript 使用React本机动画存在渲染问题,javascript,react-native,Javascript,React Native,我的动画有问题。我试图用两个不同的视图翻转卡片。当用户在两张不同的卡之间滚动时,我还试图创建一种滚动效果。当代码以下面的方式组合时,它会创建一个我无法消除的bug。我附上了一张图片,以直观地反映我的问题 谢谢你的帮助 : 我的生命周期方法: componentWillMount() { this.animatedValue = new Animated.Value(0); this.value = 0; this.animatedValue.addListener(({

我的动画有问题。我试图用两个不同的视图翻转卡片。当用户在两张不同的卡之间滚动时,我还试图创建一种滚动效果。当代码以下面的方式组合时,它会创建一个我无法消除的bug。我附上了一张图片,以直观地反映我的问题

谢谢你的帮助

:

我的生命周期方法:

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功能翻转的视图。如果您在其中一个视图中看到,则有一个名为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>
我的渲染功能:

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>
    );
  }
}
render(){
const{flashcards}=this.state;
返回(
{
返回(this.state.timer*100).toFixed(0);
}}
/>

我还创建了一个快餐店,在那里你可以看到这个问题。
您有很多问题:

  • 主要问题是,您没有正确存储每张卡的状态(如果它是否翻转)。例如,您可以添加
    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'
      }
    });
    
    import React,{Component}来自'React';
    从“react native”导入{动画、尺寸、样式表、文本、视图、TouchableOpacity、TouchableWithoutFeedback};
    从“@expo/vector icons”导入{EvilIcons,MaterialIcons};
    const SCREEN_WIDTH=尺寸.get('window').WIDTH;
    导出默认类应用程序扩展组件{
    建造师(道具){
    超级(道具);
    const flashcards=[‘konichiwa’、‘hi’、‘genki desu’、‘你好吗’];
    此.state={
    抽认卡,
    翻转:flashcards.map(()=>false),
    翻转:错误
    };
    this.flipValue=新的动画.Value(0);
    this.frontAnimatedStyle={
    转换:[{
    rotateY:this.flipValue.interpolate({
    输入范围:[0,1],
    输出范围:['0deg','180deg']
    })
    }]
    };
    this.backAnimatedStyle={
    转换:[{
    rotateY:this.flipValue.interpolate({
    输入范围:[0,1],
    输出范围:['180deg','360deg']
    })
    }]
    };
    设xOffset=new Animated.Value(0);
    this.onScroll=Animated.event(
    [{nativeEvent:{contentOffset:{x:xOffset}}}}],
    {useNativeDriver:false}
    );
    this.transitionAnimations=this.state.flashcards.map((卡片,索引)=>({
    转换:[
    {透视图:800},
    {
    比例:xOffset.interpolate({
    输入范围:[
    (索引-1)*屏幕宽度,
    索引*屏幕宽度,
    (索引+1)*屏幕宽度
    ],
    输出范围:[0.25,1,0.25]
    })
    },
    {
    rotateX:xOffset.interpolate({
    输入范围:[
    (索引-1)*屏幕宽度,
    索引*屏幕宽度,
    (索引+1)*屏幕宽度
    ],
    输出范围:['45度','0度','45度']
    })
    },
    {
    rotateY:xOffset.interpolate({
    输入范围:[
    (索引-1)*屏幕宽度,
    索引*屏幕宽度,
    (索引+1)*屏幕宽度
    ],
    输出范围:['-45度','0度','45度']
    })
    }
    ]
    }));
    }
    render(){
    返回(
    {this.state.flashcards.map(this.renderCard)}
    );
    }
    renderCard=(问题、索引)=>{
    const isfliped=this.state.fliped[index];
    返回(
    此.flipCard(索引)}>
    {(this.state.fliping | | |!isfliped)&&
    {this.state.flashcards[index]}
    }
    {(this.state.flipping | | isfliped)&
    {this.state.flashcards[index+1]}
    }
    );
    }
    flipCard=索引=>{
    如果(本状态翻转)返回;
    让isFlipped=this.state.flipped[index];
    让翻转=[…this.state.flipped];
    翻转[索引]=!已翻转;
    这是我的国家({
    翻转:是的,
    轻弹
    });
    this.flipValue.setValue(isfliped?1:0);
    动画.spring(this.flipValue{
    toValue:isFlipped?0:1,
    摩擦:8,
    张力:10
    }).start(()=>{
    this.setState({flipping:false});
    });
    }
    }
    const styles=StyleSheet.create({
    容器:{
    背景颜色:'红色',
    弹性:1,
    flexDirection:'列',
    justifyContent:“间距”
    },
    滚动视图:{
    flexDirection:'行',
    背景颜色:“黑色”
    },
    滚动页面:{
    宽度:屏幕宽度,
    填充:20
    },
    屏幕:{
    身高:400,
    为内容辩护:“中心”,
    对齐项目:“居中”,
    边界半径:25,
    背景颜色:“白色”,
    宽度:屏幕宽度-20*2,
    背面可见性:“隐藏”
    },
    正文:{
    尺码:45,
    fontWeight:“粗体”
    },
    iconStyle:{
    flexDirection:'行',
    为内容辩护:“中心”
    },
    背面:{
    位置:'绝对',
    排名:0,
    背面可见性:“隐藏”
    }
    });
    
    至少现在它工作正常。

    ma
    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'
      }
    });