对滚动视图的动态高度进行动画处理

时间:2018-11-13 13:40:08

标签: javascript reactjs react-native

我的用户界面类似于下图:

Animatable UI

标题部分(深蓝色部分)应该是固定的,而正文部分是可滚动的。

“标题”部分的高度将是动态的,因为可以从服务器检索到4-5个项目符号。

我的任务是,当用户开始向上滚动以阅读下面显示的内容时,开始减小标题部分的高度,直到用户只能阅读标题中的标题为止。另外,当用户开始向下滚动到他/她可以看到标题 Body 的位置时,我应该开始增加高度,以便他可以再次看到所有项目符号。

此功能更像是在滚动条上隐藏/显示标题。但是,标题将始终可见。

我已经写了下面的代码来实现相同的目的:

import React from 'react';
import {
  View,
  Text,
  ScrollView,
  TouchableOpacity,
  Linking,
  Image,
  Animated,
} from 'react-native';

class App extends React.PureComponent<Props, States> {
  constructor() {
    super();

    this.state = {
      headerHeight: 0,
      headerTitleHeight: 0,
    };

    this.scrollY = new Animated.Value(0);
  }

  render() {
    const { navigation, pending, macroFeed } = this.props;
    const { headerHeight, headerTitleHeight } = this.state;

    const { themeDetails } = navigation.state.params;
    const {
      title, bullet1, bullet2, bullet3,
    } = themeDetails;

    let headerStyle = {};

    if (headerHeight) {
      headerStyle = {
        height: this.scrollY.interpolate({
          inputRange: [0, headerHeight - headerTitleHeight],
          outputRange: [headerHeight, headerTitleHeight],
          extrapolate: 'clamp',
        }),
      };
    }

    const sortedMacroFeed = _.sortBy(macroFeed, (o) => moment(o.date).format('YYYYMMDD')).reverse().slice(0, 5);

    if (pending) {
      return (
        <View style={styles.maxFlex}>
          <LoadingSpinner
            size="large"
            containerStyle={styles.loadingSpinner}
          />
        </View>
      );
    }

    return (
      <View
        style={styles.maxFlex}
      >
        <Animated.View
          style={[styles.headerWrapper, headerStyle]}
          onLayout={(event) => {
            this.setState({
              headerHeight: event.nativeEvent.layout.height,
            });
          }}
        >
          <View style={styles.macroBgWrapper}>
            <Image source={themeDetails.imgUrl} style={styles.macroBg} />
            <View style={styles.macroBgOverlay} />
          </View>

          <View 
            style={styles.header}
          onLayout={(event) => {
            this.setState({
              headerTitleHeight: event.nativeEvent.layout.height,
            });
          }}
          >
            <TouchableOpacity onPress={() => navigation.goBack()}>
              <View>
                <Icon name="ios-arrow-back" size={32} style={styles.backIcon} />
              </View>
            </TouchableOpacity>

            <View style={styles.titleWrap}>
              <Text style={styles.headerTitle}>
                {title}
              </Text>
            </View>
          </View>

          <View style={styles.bulletWrapper}>
            {
              !!bullet1 && (
                <View style={styles.column}>
                  <View style={styles.row}>
                    <View style={styles.bullet}>
                      <Text style={styles.buttetListText}>
                        {'\u2022'}
                        {' '}
                      </Text>
                    </View>
                    <View style={styles.bulletText}>
                      <Text style={styles.buttetListText}>
                        {bullet1}
                      </Text>
                    </View>
                  </View>
                </View>
              )
            }

            {
              !!bullet2 && (
                <View style={styles.column}>
                  <View style={styles.row}>
                    <View style={styles.bullet}>
                      <Text style={styles.buttetListText}>
                        {'\u2022'}
                        {' '}
                      </Text>
                    </View>
                    <View style={styles.bulletText}>
                      <Text style={styles.buttetListText}>
                        {bullet2}
                      </Text>
                    </View>
                  </View>
                </View>
              )
            }

            {
              !!bullet3 && (
                <View style={styles.column}>
                  <View style={styles.row}>
                    <View style={styles.bullet}>
                      <Text style={styles.buttetListText}>
                        {'\u2022'}
                        {' '}
                      </Text>
                    </View>
                    <View style={styles.bulletText}>
                      <Text style={styles.buttetListText}>
                        {bullet3}
                      </Text>
                    </View>
                  </View>
                </View>
              )
            }

            {
              !bullet1 && !bullet2 && !bullet3 && (
                <View style={styles.noBulletWrapper}>
                  <Text style={styles.noBulletPoints}>
                    No description found.
                  </Text>
                </View>
              )
            }
          </View>
        </Animated.View>

        <ScrollView
          style={styles.maxFlex}
          showsVerticalScrollIndicator={false}
          onScroll={Animated.event([
            { nativeEvent: { contentOffset: { y: this.scrollY } } },
          ])}
          scrollEventThrottle={16}
        >
          <View style={[styles.section, styles.wrapGutter]}>
            <Text style={styles.sectionTitle}>
              Recent Headlines
            </Text>

            {
              sortedMacroFeed.map((feed) => (
                <View key={feed.id} style={styles.newsSection}>
                  <Text style={styles.newsHours}>
                    {moment(feed.date).fromNow()} | {feed.author}
                  </Text>

                  <Text style={styles.newsTitle}>
                    {feed.title}

                    <Text onPress={() => this.openWebView(feed.url)}>
                      <EvilIcons name="external-link" size={16} style={styles.externalLinkIcon} />
                    </Text>
                  </Text>
                </View>
              ))
            }
          </View>

          <View style={styles.section}>
            <View style={[styles.wrapGutter, styles.sectionTitleWrap]}>
              <Text style={styles.sectionTitle}>
                Exposure
              </Text>

              <View style={styles.totalNavWrap}>
                <Text style={styles.totalNav}>
                  $467M
                </Text>

                <Text style={styles.totalNavLabel}>
                  Total NaV (all portfolios)
                </Text>
              </View>
            </View>

            <Tabs
              tabsData={TABS_DATA}
              renderTabContent={this.renderTabContent}
              tabName="title"
              hideIfOneTab
            />
          </View>
        </ScrollView>
      </View>
    );
  }
}

如果您已观察到,我正在尝试使用onLayout检索标题部分的动态高度。

此代码仅以一种方式起作用,即当我向上滚动时。标题部分的高度减小到只能看到标题且隐藏项目符号点的程度。但是在那之后,我无法向下滚动。高度会永久降低。

现在,如果我更改下面给出的代码:

let headerStyle = {};

if (headerHeight) {
  headerStyle = {
    height: this.scrollY.interpolate({
      inputRange: [0, headerHeight - headerTitleHeight],
      outputRange: [headerHeight, headerTitleHeight],
      extrapolate: 'clamp',
    }),
  };
}

const headerStyle = {
    height: this.scrollY.interpolate({
      inputRange: [0, 240],
      outputRange: [300, 60],
      extrapolate: 'clamp',
    }),
};

一切似乎都正常。基本上,如果我停止动态获取高度值并提供静态值(例如240之类的值),那么一切似乎都可以正常工作。

但是,如果我接受动态高度,则滚动动画将停止。任何解决此问题的帮助将不胜感激。谢谢您。

1 个答案:

答案 0 :(得分:0)

我发现了问题。因此,如果您观察到这行:

        <Animated.View
          style={[styles.headerWrapper, headerStyle]}
          onLayout={(event) => {
              this.setState({
                headerHeight: event.nativeEvent.layout.height,
              });
          }}
        >

onLayout用于setState,一旦headerHeight值在滚动上更改。由于这个原因,无法使用标题的高度来缩小和向下滚动内容。

我将代码更改为:

        <Animated.View
          style={[styles.headerWrapper, headerStyle]}
          onLayout={(event) => {
            if (!this.state.headerHeight) {
              this.setState({
                headerHeight: event.nativeEvent.layout.height,
              });
            }
          }}
        >

现在,一切正常。