首页 > 解决方案 > 在 React Native 中更新屏幕上的内容会使动画组件冻结

问题描述

我的反应原生应用程序的主屏幕具有滚动动画的组件。但是,对屏幕的任何微小更改都会将组件重置为其默认状态,并且它们不再具有动画效果。

我正在尝试获取数据并将其显示在主屏幕上。每当加载新内容时,动画组件都会恢复到其原始状态。

主页/index.tsx

import React, { useEffect } from 'react'
import { View, StyleSheet, StatusBar, Image } from 'react-native'
import Animated, { Extrapolate } from 'react-native-reanimated'
import { useDispatch, useSelector } from 'react-redux'
import { bannerImage, logoImage } from '../../assets/images'
import CategoryContainer from './CategoryContainer'
import colors from '../../styles/colors'
import CstmBigDisplayButton from '../../components/CstmBigDisplayButton'
import globalStyles from '../../styles/globals'
import MenuButton from '../../components/MenuButton'
import TranslucentStatusBar from '../../components/TranslucentStatusBar'
import { getCategories } from '../../redux/actions/categoryAction'
import { RootState } from '../../redux/types'

const HEADER_MAX_HEIGHT = 430
const HEADER_MIN_HEIGHT = 100

const Home = ({ navigation }: any) => {
    const { categories } = useSelector((state: RootState) => state.categories)
    const dispatch = useDispatch()
    useEffect(() => {
        dispatch(getCategories())
    }, [])
    let scrollY = new Animated.Value(0)
    const headerHeight = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT],
            outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT],
            extrapolate: Extrapolate.CLAMP,
        }
    )
    const animateOverlay = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT / 2, HEADER_MAX_HEIGHT - 30],
            outputRange: [1, 0.5, 0.1],
            extrapolate: Extrapolate.CLAMP,
        }
    )
    const menuOpacity = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT - 30],
            outputRange: [0.5, 1],
            extrapolate: Extrapolate.CLAMP
        }
    )

    return (
    <>
        <TranslucentStatusBar />
        <Animated.View
            style={[
                styles.header,
                {
                    height: headerHeight,
                    elevation: 10
                }
            ]}
        >
            <View style={styles.headerBackground} >
                <Animated.View
                    style={{
                        position: 'absolute',
                        top: 0,
                        right: 0,
                        zIndex: 11,
                        marginTop: StatusBar.currentHeight,
                        opacity: menuOpacity,
                    }}
                >
                    <View style={{ margin: 16 }}>
                        <MenuButton onPress={() => {navigation.openDrawer()}}/>
                    </View>
                </Animated.View>
                <Animated.Image
                    source={bannerImage}
                    resizeMode='cover'
                    style={{
                        position: 'absolute',
                        left: 0,
                        right: 0,
                        top: 0,
                        height: headerHeight,
                        opacity: animateOverlay,
                }}/>
                <Animated.View
                    style={[ 
                        styles.overlay,
                        {
                            backgroundColor: animateOverlay
                        }
                    ]}
                >
                    <Image
                        source={logoImage}
                        style={styles.logo}
                        resizeMode='contain'
                    />
                </Animated.View>
            </View>
        </Animated.View>
        <Animated.ScrollView
            scrollEventThrottle={16}
            style={globalStyles.screenDefaults}
            onScroll={Animated.event(
                [{ nativeEvent: { contentOffset: { y: scrollY } } }],
                {
                    useNativeDriver: true,
                    listener: (event: any) => console.log(event)
                }
            )}
        >
            <View style={styles.contentContainer}>
                <CategoryContainer
                    titleStyle={[styles.general]}
                    titleText='General Categories'
                    titleTextStyle={{ color: colors.primary["500TextColor"] }}
                    categories={categories.filter((category: any) => !category.special)}
                    navigation={navigation}
                />
                <View style={styles.divider}></View>
                <CategoryContainer
                    titleStyle={[styles.special]}
                    titleText='Special Categories'
                    titleTextStyle={{ color: colors.secondary["700TextColor"] }}
                    categories={categories.filter((category: any) => category.special)}
                    navigation={navigation}
                />
            </View>
            <CstmBigDisplayButton />
        </Animated.ScrollView>
    </>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    header: {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        height: HEADER_MAX_HEIGHT,
        backgroundColor: 'grey',
        zIndex: 10,
        alignContent: 'center',
        justifyContent: 'center'
    },
    headerBackground: {
        width: '100%',
        flex: 1,
        backgroundColor: '#FFF'
    },
    logo: {
        flex: 1,
        height: undefined,
        width: undefined,
    },
    overlay: {
        flex: 1,
        paddingTop: StatusBar.currentHeight
    },
    contentContainer: {
        flexDirection: 'row',
        flex: 1,
        paddingTop: HEADER_MAX_HEIGHT,
        paddingBottom: 24
    },
    general: {
        backgroundColor: colors.primary[500],
        color: colors.primary["500TextColor"]
    },
    special: {
        backgroundColor: colors.secondary[700],
        color: colors.secondary["700TextColor"]
    },
    divider: {
        backgroundColor: '#909090',
        height: '99%',
        width: '0.1%'
    },
    
})

export default Home

全局变量.ts

import { StyleSheet, StatusBar } from 'react-native' 
import theme, { standardInterval } from './theme'

const globalStyles = StyleSheet.create({
  gutterBottom: {
    marginBottom: 8
  },
  captionText: {
    fontSize: 12
  },
  screenDefaults: {
    flex: 1,
        backgroundColor: theme.pageBackgroundColor,
  },
  headlessScreenDefaults: {
        paddingTop: StatusBar.currentHeight,
        padding: 16
    },
  iconText: {
    flexDirection: 'row',
    alignItems: 'center'
  },
  iconTextMargin: {
    marginLeft: 4
  },
  label: {
    fontSize: 16,
    marginBottom: 4,
    paddingLeft: 12,
    color: '#555'
  },
  textInput: {
    padding: 0,
    fontSize: 16,
    color: '#444',
    marginLeft: 6,
    marginRight: 8,
    flex: 1
  },
  textInputContainer: {
    borderWidth: 1,
    borderColor: '#ddd',
    backgroundColor: 'white',
    borderRadius: standardInterval(0.5),
    padding: 8,
    flexDirection: 'row',
    alignItems: 'center'
  },
  helperText: {
    marginTop: 4,
    paddingLeft: 12,
    fontSize: 12,
    color: '#555'
  },
  button: {
    padding: standardInterval(1.5),
    borderRadius: standardInterval(.5),
    // marginLeft: 12,
  },
})

export default globalStyles

值得注意的点。 当从抽屉导航组件中获取内容时,动画组件工作得很好。这是不可靠的,因为如果在已安装主屏幕时接收到数据,则组件将不会动画。

抽屉导航.tsx

import React, { useEffect } from 'react'
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem, DrawerContentComponentProps,  } from '@react-navigation/drawer'
import { NavigationContainer } from '@react-navigation/native'
import { useSelector, useDispatch } from 'react-redux'
import AuthNavigator from './AuthNavigator'
import MainNavigator from './MainNavigator'
import theme from '../styles/theme'
import colors from '../styles/colors'
import Profile from '../screens/profile'
import { RootState } from '../redux/types'
import { verifyToken } from '../redux/actions/authAction'
import Splash from '../screens/splash'
import { logOut } from '../redux/actions/authAction'
import { getMetadata } from '../redux/actions/metadataActions'
import { getCategories } from '../redux/actions/categoryAction'

const Drawer = createDrawerNavigator()

const DrawerNavigator = () => {
  const dispatch = useDispatch()
  const { hasInitiallyLoaded, authenticationToken, authenticatedUser } = useSelector((state: RootState) => state.auth)
  useEffect(() => {
    if (!hasInitiallyLoaded) dispatch(verifyToken(authenticationToken))
  })
  useEffect(() => {
    dispatch(getMetadata())
    dispatch(getCategories())
  }, [getMetadata])
  
  return (
    <>
      {
        hasInitiallyLoaded
          ? <NavigationContainer>
              <Drawer.Navigator
                drawerStyle={{ backgroundColor: theme.pageBackgroundColor }}
                drawerContentOptions={{ activeTintColor: colors.secondary[500] }}
                drawerContent={(props: DrawerContentComponentProps) => (
                  <DrawerContentScrollView {...props}>
                    <DrawerItemList {...props} />
                    {
                      authenticatedUser
                        && <DrawerItem
                              label='log out'
                              onPress={() => {dispatch(logOut([() => props.navigation.navigate('home')]))}}
                            />
                    }
                  </DrawerContentScrollView>
                )}
              >
                <Drawer.Screen name='home' component={MainNavigator} />
                {
                  authenticatedUser
                    ? <Drawer.Screen name='profile' component={Profile} />
                    : <Drawer.Screen name='login' component={AuthNavigator} />
                }
              </Drawer.Navigator>
            </NavigationContainer>
          : <Splash />
      }
    </>
  )
}

export default DrawerNavigator

Animated.event()此外,中的侦听Animated.ScrollView器不起作用

Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
    {
        useNativeDriver: true,
        listener: (event: any) => console.log(event)
    }
)}

标签: javascriptreactjsreact-nativereduxreact-native-reanimated

解决方案


将 scrollY 视图置于状态后,一切似乎都按预期工作。


推荐阅读