React Native: Анимация сокращающегося заголовка с вкладками

Цель

Im пытается создать представление с анимированным сокращающимся заголовком с вкладкой, содержащей вкладки с прокручиваемым контентом. См. Изображение.

Настройка

Я использую интерактивную навигацию с помощью TabNavigator. Заголовок представляет собой компонент с фиксированной высотой, в настоящее время над TabNavigator. Заголовок загорается над вкладками все время, занимая драгоценное пространство.

Пробные подходы

  • Я пробовал Janic Duplessis Blog Post, но я не могу заставить его работать из-за вкладок.

  • Я также попытался реализовать его с помощью двух ScrollView/FlatList: один обернутый вокруг всего представления, и одна упаковка содержимого, но я не могу получить ответ на родной для распространения края прокрутки.

Желаемый эффект такой же, как в магазине Google Play.

Мобильная скриншот Google play

Ответы

Ответ 1

Мне удалось осуществить это с Animated.diffClamp - вы можете проверить код здесь. Я написал статью, подробно объясняющую код здесь

* Я использую пакет native-base для компонентов пользовательского интерфейса (для упрощения кода UI), но интерполяции/определения анимации одинаковы с или без него.

Demo

Ответ 2

Вы можете добиться этого, самостоятельно управляя действиями анимированных прокруток, используя компонент RN-Animated. Это не совсем то, что вы просили, но его можно легко воспроизвести, чтобы получить правильные результаты, и вам действительно нужно использовать метод renderScrollComponent любого вида списка, чтобы захватить эту же вещь... так... Вот некоторый код:

Код

constructor(props) {
    super(props);

    this.headerHeight = 150;

    this.state = {
        scrollY: new Animated.Value(0),
    };
}

render() {
    return (
        <View style={[
                styles.container,
                {
                    width: this.props.display.width,
                    height: this.props.display.height,
                }
            ]}>

            { this.renderScrollView() }
            { this.renderListHeader() }

        </View>
    );
}


renderListHeader() {
    return (
        <Animated.View
            style={[
                styles.headerContainer,
                {
                    width: this.props.display.width,
                    height: this.state.scrollY.interpolate({
                        inputRange: [0, this.headerHeight],
                        outputRange: [this.headerHeight, this.headerHeight / 2],
                        extrapolate: 'clamp',
                    }),
                }
            ]}>

            <Text style={[bodyFontStyle, styles.testingText]}>
                I am a test! 
            </Text>
        </Animated.View>
    );
}


renderScrollView() {
    return (
        <Animated.ScrollView
            bounces={false}
            scrollEventThrottle={1}
            showsVerticalScrollIndicator={false}
            showsHorizontalScrollIndicator={false}
            onScroll={Animated.event( [{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }] )}>

            <Animated.View
                style={[
                    styles.innerScrollContainer,
                    {
                        height: 800,
                        width: this.props.display.width,
                        marginTop: this.headerHeight,
                    }
                ]}>

                <Text style={[bodyFontStyle, styles.innerContainerText]}>
                    Look ma! No Hands!
                </Text>
            </Animated.View>
        </Animated.ScrollView>
    );
}


const styles = StyleSheet.create({
    container: {
        position: 'relative',
        backgroundColor: 'transparent',
    },
    headerContainer: {
        position: 'absolute',
        top: 0, left: 0,
        overflow: 'hidden',
        backgroundColor: 'green',
   },
   innerScrollContainer: {
        position: 'relative',
        backgroundColor: 'white',
   },
   innerContainerText: { color: 'black' },
   testingText: { color: 'black' },
});

Я уверен, что вы поняли, что этот код пытается сделать... но просто выложите на него пробой.

Разбивка кода

Я использую реквизиты this.props.display.width/height для отправки ширины/высоты системного уровня ширины/высоты экрана устройства, но если вы предпочитаете использовать flex, то вместо этого.

Вам не обязательно нужен жесткий или статический размер заголовка, просто центральное место (например, уровень класса или уровень JS-модуля), чтобы использовать его в коде вашего класса.

Наконец, просто потому, что я интерполировал данные прокрутки на высоту окна, это не значит, что вам нужно. В примере, который вы показываете, он больше похож на то, что вы хотели бы перевести окно вверх, а не уменьшать его высоту.

Ответ 3

Я пытаюсь сделать подобное, но не добился успеха. Было бы намного легче, если бы у нас не было вкладок. Зачем? Каждая вкладка отображает scrollview/flatlist или аналогичную, и мы прикрепляем к ней одинаковый scrollY на обоих flatlist. Поэтому, когда мы начинаем прокрутку на tab1 и переходим к tab2, он перезапускает позицию заголовка, и это выглядит очень плохо. Я пробовал другой подход без каких-либо успехов. Самое близкое решение - это эта статья, но салфетки не работают и не уверены, можем ли мы ее реализовать. https://blog.expo.io/implementation-complex-animation-in-react-native-by-example-search-bar-with-tab-view-and-collapsing-68bb43be2dcb. Любая помощь или совет были бы замечательными...