Как анимировать View, закрепленный в определенной позиции при прокрутке в React Native? - PullRequest
0 голосов
/ 07 мая 2020

Я пытаюсь анимировать View, чтобы он был привязан к определенной позиции при прокрутке. Компонент прокрутки, который я использую: FlatList.

Вот что я до сих пор сделал:

ProfileScreen

По сути, это структура моего ProfileScreen.js:

<View>
    <Animated.View /> --> The animated cover photo (includes the animated avatar as well)

    <Animated.FlatList /> --> This renders the list of images you see in the video. And a ListHeaderComponent renders the content from the name to the View of follow numbers
</View

Теперь я хочу достичь:

, когда пользователь прокручивает весь FlatList и достигает положения, в котором View, содержащий последующие числа , достигает нижней границы заголовка , что следовать View будет там. (следующий View должен прокручиваться вместе с th FlatList)

Точно так же, как вид с липкой вкладкой.

Вы можете представить, что мой экран «цели» похож на экран профиля Twitter.

Вот полный код:

//...
const ProfileScreen = (props) => {

    const [index, setIndex] = useState(0);
    const [isFollowed, setIsFollowed] = useState(false);
    const [firstFollow, setFirstFollow] = useState(false);
    const [enableScrollView, setEnableScrollView] = useState(false);
    const trips = useSelector(state => state.trips.availableTrips);
    const scrollY = useRef(new Animated.Value(0)).current;

    const headerHeight = scrollY.interpolate({
        inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT], // [0, 50]
        outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT], // [120,70]
        extrapolate: 'clamp'
    });

    const profileImageHeight = scrollY.interpolate({
        inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT-10],
        outputRange: [PROFILE_IMAGE_MAX_HEIGHT,PROFILE_IMAGE_MIN_HEIGHT],
        extrapolate: 'clamp'
    });
  
    const profileImageMarginTop = scrollY.interpolate({
        inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
        outputRange: [
          HEADER_MAX_HEIGHT - PROFILE_IMAGE_MAX_HEIGHT / 2,
          HEADER_MIN_HEIGHT /2 - PROFILE_IMAGE_MIN_HEIGHT/2
        ],
        extrapolate: 'clamp'
    });
    const profileImageMarginLeft = scrollY.interpolate({
        inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
        outputRange: [
            WIDTH/2 - PROFILE_IMAGE_MAX_HEIGHT/2,
            10
        ],
        extrapolate: 'clamp'
    })
    const headerZindex = scrollY.interpolate({
        inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT, 120],
        outputRange: [0, 0, 1000],
        extrapolate: 'clamp'
    });
  
    const headerTitleColor = scrollY.interpolate({
        inputRange: [0, 50, 70, 80, 90, 100],
        outputRange: ['transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'white'],
        extrapolate: 'extend'
    });

    const tabBarPosition = scrollY.interpolate({
        inputRange: [0,200],
        outputRange: [(HEADER_MAX_HEIGHT + PROFILE_IMAGE_MIN_HEIGHT)*2, HEADER_MIN_HEIGHT],
        extrapolate: 'clamp'
    })


    const _render_Sitcky_Info_View = () => {
        return(
            <View style={{marginTop: HEADER_MAX_HEIGHT+ PROFILE_IMAGE_MAX_HEIGHT/2}}>
                <InfoDisplay
                    isFollowed={isFollowed}
                    onFollow={onFollow}
                    userName={profile.userName}
                    tripsNumber={trips.length}
                    navigateToTripsListScreen={() => props.navigation.navigate('TripsListScreen')}
                />
                
            </View>
        )
    }

    return(
        <SafeAreaView style={styles.container}>
            <Animated.View style={[styles.backgroundImage, {
                height: headerHeight,
                zIndex: headerZindex,
                elevation: headerZindex,
                justifyContent: 'center'
            }]}>
                <Animated.Image 
                    source={require('../../../assets/images/beach.jpg')} 
                    style={{
                        flex: 1,
                    }}
                />
                <View style={{
                    position: 'absolute',
                    left: 60,
                }}>
                    <Animated.Text style={{color: headerTitleColor, fontSize: 20, fontWeight: 'bold'}}>{profile.userName}</Animated.Text>
                </View>                
                <Animated.View style={[styles.profileImgContainer,{
                    height: profileImageHeight,
                    width: profileImageHeight,
                    borderRadius: PROFILE_IMAGE_MAX_HEIGHT/2,
                    position: 'absolute',
                    top: profileImageMarginTop,
                    left: profileImageMarginLeft,
                    alignSelf: 'center'
                }]}>                         
                    <Image source={{uri: profile.userAvatar}} style={styles.profileImg} />
                </Animated.View>
            </Animated.View>
            
            <Animated.FlatList
                style={{flex: 1, backgroundColor: 'transparent'}}
                contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
                scrollEventThrottle={16}
                onScroll={Animated.event(
                    [{nativeEvent: {contentOffset: {y: scrollY}}}],
                    
                )}
                showsVerticalScrollIndicator={false}
                bounces={true}
                data={imgData}
                numColumns={NUM_COLUMNS}
                keyExtractor={item => item.id}
                renderItem={(itemData) => {
                    return(
                        <TouchableOpacity style={{
                            marginHorizontal: findMidOfThree(imgData, itemData.index) ? 0 : 5 ,
                            marginVertical: 3,
                            shadowColor: 'black',
                            shadowOpacity: 0.26,
                            shadowOffset: { width: 0, height: 2 },
                            shadowRadius: 8,
                            elevation: 7,
                    }}>
                            <Image source={itemData.item.source} style={{
                                height: WIDTH/3.5, 
                                width: WIDTH/3.5,
                                borderColor: 'white',
                                borderWidth: 2,
                                borderRadius: 10,
                                
                            }} />
                        </TouchableOpacity>
                    )
                }}
                ListHeaderComponent={_render_Sitcky_Info_View}
                {...props}
            >
                
                
            </Animated.FlatList>
        </SafeAreaView>
    );
};

export default ProfileScreen;

const styles = StyleSheet.create({
//...

})

InfoDisplay содержит следующие числа View. Я знаю, что мне придется отделить его и перенести в ProfileScreen. Я также попытался оживить его с помощью tabBarPosition, но это не сработало, как я думал

Вы можете представить, что мой экран «цели» похож на экран профиля Twitter, на котором мои подписаны числа View действует как вкладка в Twitter

https://drive.google.com/file/d/1ERt_7gnPgiwXPg-WODXSnrOZ10kQgPQl/view?usp=drivesdk

ПОМОГИТЕ мне, пожалуйста. Буду очень признателен!

1 Ответ

1 голос
/ 21 мая 2020

Рассчитайте расстояние, необходимое для перемещения по Follow View , как показано на изображении (пусть это будет x).

image

Здесь я беру 500 в качестве смещения. вы можете выбрать любое число больше x. (или используйте высоту экрана )

const range = [x, x+500];
const translateY = scrollY.interpolate({inputRange:range, outputRange:range, extrapolateLeft: 'clamp'});

ie, если x = 200;

const translateY = scrollY.interpolate({inputRange:[200, 700], outputRange:[200, 700], extrapolateLeft: 'clamp'});

logi c заключается в том, что когда последующий вид достигает вершины при прокрутке (ie, расстояние перемещения x). он начнет переводиться вниз с той же скоростью прокрутки.

Предоставьте этот переводY следующему виду.

<Animated.View style={{...styles, transform: [{translateY}]}} />  // your  follow View

Надеюсь, это поможет вам!

...