反应原生模态时间延迟和生涩的动画

Posted

技术标签:

【中文标题】反应原生模态时间延迟和生涩的动画【英文标题】:react native modal time delay and jerky animation 【发布时间】:2020-07-22 10:37:40 【问题描述】:

我正在尝试构建一个具有左右轮播的反应原生屏幕,在轮播的每个面板中都有一个带有项目列表的垂直平面列表。最多有 8-10 个轮播面板和 5-30 个项目在平面垂直平面列表中,因此在渲染的最多项目中可能有 300 个,但通常是 100 个。

我正在调用 API 并每隔 2-3 秒检查一次服务器上的数据,并使用新数据设置组件中的状态。这目前有效,并且子组件中的数据数据会按应有的方式更新。

平面列表中的每个项目都是可点击的,这会触发在页面中启动的模式弹出窗口。我遇到的问题是模式弹出窗口需要 4-5 秒才能出现和关闭。此外,当模态框最终开始消失时,动画会变得生涩,并且深色背景层似乎会在移除时闪烁。

我首先尝试了内置 modal 并且还使用了 react-native-modal 包,两者都是相同的。

我尝试使用 InteractionManager.runAfterInteractions 和 shouldComponentUpdate(nextProps, nextState) 尝试阻止我的 api 调用,直到动画完成或阻止我的 isModalVisible 状态属性在我更改它时导致重新渲染。

下面的代码,任何帮助将不胜感激。

import 
    Text,
    Button,
    StyleSheet,
    View,   
    FlatList,   
    Dimensions,
    Image,
    Animated,
    SafeAreaView,
    TouchableHighlight,
    InteractionManager,
 from 'react-native';
import React from 'react';
import Title from './Title';
import CarouselMeeting from './CarouselMeeting';
import Modal from 'react-native-modal';
import Carousel from 'react-native-snap-carousel';

class MeetingDisplay extends React.Component 
constructor(props) 
    super(props);        
    this.state = 
        raceList: [],
        meetingList: [],
        meetingId: props.navigation.state.params.meetingId,
        currentIndex: 0,
        raceCount: 0,
        activeIndex: 0,
        isModalVisible: false,
    
    this.refreshScreen = this.refreshScreen.bind(this)


componentDidMount() 
    InteractionManager.runAfterInteractions(() => 
    Promise.all([fetch('http://apicallurl?id' + this.state.meetingId), fetch('http://apicallurl?id' + this.state.meetingId)])
        .then(([res1, res2]) => 
            return Promise.all([res1.json(), res2.json()])
        )
        .then(([res1, res2]) => 
            this.setState(
                raceList: res1,
                meetingList: res2.Meets,

            )
        );
    this.interval = setInterval(() => this.updateRaceList(), 3000);
);


componentDidUpdate(prevProps, prevState) 
    InteractionManager.runAfterInteractions(() => 
    if (prevState.meetingId !== this.state.meetingId) 
        Promise.all([fetch('http://apicallurl?id' + this.state.meetingId), fetch('http://apicallurl?id' + this.state.meetingId)])

            .then(([res1, res2]) => 
                return Promise.all([res1.json(), res2.json()])
            )
            .then(([res1, res2]) => 
                this.setState(
                    raceList: res1,
                    meetingList: res2.Meets,

                )
            );
    
);


async updateRaceList() 
    InteractionManager.runAfterInteractions(() => 
        fetch('http://apicallurl' + this.state.meetingId)
            .then((response) => response.json())
            .then((responseJson) => 
                this.setState(
                    raceList: responseJson,
                , function () 

                );
            )
            .catch((error) => 
                console.error(error);
            );
    );


toggleModal = () => 
    InteractionManager.runAfterInteractions(() => 
        this.setState( isModalVisible: !this.state.isModalVisible );
    );
;

shouldComponentUpdate(nextProps, nextState) 
    if(this.state.isModalVisible !== nextState.isModalVisible)
     this.setState( isModalVisible: nextState.isModalVisible)
     return false;
     else return true;
  

render() 
    const peek = 20;
    const gutter = peek / 4;
    const cardWidth = Dimensions.get('window').width - gutter * 2 - peek * 2;
    const contentOffset = (Dimensions.get('window').width - (cardWidth + (gutter * 2))) / 2;

    return (
        <>
            <Title heading=this.state.raceList.VenueName />                

            <SafeAreaView style= flex: 1, backgroundColor: 'rebeccapurple', paddingTop: 50, >
                <View style= flex: 1, flexDirection: 'row', justifyContent: 'center', >
                    <Carousel
                        layout="default"
                        useScrollView
                        ref=ref => this.Carousel = ref
                        data=this.state.raceList.RaceList
                        sliderWidth=cardWidth
                        itemWidth=cardWidth - gutter * 2 - peek * 2
                        onSnapToItem=index => this.setState( activeIndex: index )
                        renderItem=( item ) => (
                            <Animated.View style=
                                flex: 1,
                                paddingTop: 20,
                                width: cardWidth,                                    
                                margin: gutter,
                                backgroundColor: 'blue',
                                justifyContent: 'center',
                                alignItems: 'center',
                            >
                                <FlatList
                                    horizontal=false
                                    showsVerticalScrollIndicator=true
                                    legacyImplementation=false

                                    data=item.runner_list
                                    keyExtractor=(item, index) => index.toString()
                                    renderItem=( item , index) =>
                                        <TouchableHighlight style= flex: 1, flexDirection: 'row'  onPress=this.toggleModal >
                                            <Image style= width: 50, height: 50  source= uri: item.imageurl  />                                                
                                        </TouchableHighlight>
                                >
                                </FlatList>
                            </Animated.View>
                        )
                    />
                </View>
            </SafeAreaView>

            <Modal isVisible=this.state.isModalVisible
                    backdropTransitionOutTiming=1>
                <View style= flex: 1 >
                    <Text>Hello!</Text>
                    <Button title="Hide modal" onPress=this.toggleModal />
                </View>
            </Modal>
        </>
    );



const styles = StyleSheet.create(
    centeredView: 
        flex: 1,
        justifyContent: "center",
    alignItems: "center",
    marginTop: 22
,
modalView: 
    margin: 20,
    backgroundColor: "white",
    borderRadius: 20,
    padding: 35,
    alignItems: "center",
    shadowColor: "#000",
    shadowOffset: 
        width: 0,
        height: 2
    ,
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    elevation: 5
,
openButton: 
    backgroundColor: "#F194FF",
    borderRadius: 20,
    padding: 10,
    elevation: 2
,
textStyle: 
    color: "white",
    fontWeight: "bold",
    textAlign: "center"
,
modalText: 
    marginBottom: 15,
    textAlign: "center"

);

export default MeetingDisplay;

尝试 1

我有一个想法,这可能是我使用了一个名为“react-native-snap-carousel”的第三方轮播库,所以我试图用一个看起来很糟糕的滚动视图来替换它,并让我的所有平面列表/项目都在其中呈现但这并没有改善弹出时间延迟,仍然是 2-3 秒。

尝试 2

我发现了一个叫做 react.purecomponent 的东西,它可能应该对状态/道具进行浅层比较,并且只在项目/状态实际发生变化时触发重新渲染,这可能意味着动画/ui线程无论是什么导致问题停止。但是在模态显示之前仍然没有更好的暂停(无论是在模拟器上还是在设备上)

class MeetingDisplay extends React.PureComponent

尝试 4

通过在底部轮播下方的页面底部放置一个按钮来触发平面列表之外的模式,从而将平面列表排除在外。

....</View>
            </SafeAreaView>

            <Modal                    
                visible=this.state.isModalVisible
                backdropTransitionOutTiming=1
            >
                <View style= flex: 1 >
                    <Text>Hello!</Text>
                    <Button title="Hide modal" onPress=this.toggleModal />
                </View>
            </Modal>
            <Button title="Show modal" onPress=this.toggleModal />                
        </>
    );....

这并没有导致任何改进或性能。那么还有什么导致这个问题。是因为间隔导致我的组件不断重新渲染吗?因此,必须有一种方法可以暂停我缺少的组件重新渲染。有人吗?

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,并通过将以下道具添加到模态来解决它

       useNativeDriver=true 

请参考这个对我有帮助的答案, https://***.com/a/62020910/10432212

【讨论】:

非常感谢!!这是解决我问题的唯一方法

以上是关于反应原生模态时间延迟和生涩的动画的主要内容,如果未能解决你的问题,请参考以下文章

TouchableOpacity 和按钮在反应原生模态中不起作用?

模态打开时使抽屉可见。反应原生

ReactJS - 使用反应钩子防止模态的初始动画

反应原生:无法将自定义高度设置为模态

如何在本机反应中关闭模态

如何使用反应原生 WebView 在模式中显示网页?