如何使用ScrollView实现与原生作用的scrollspy?

时间:2018-05-14 06:43:52

标签: javascript reactjs react-native

我有一个ScrollView组件,其中包含不同的元素:

<ScrollView>

    <Item1/>

    <Item2/>

    <Item3/>

</ScrollView>

我现在有一个标签,我需要根据用户在滚动视图中查看的当前部分突出显示每个标签项。例如,如果用户在滚动视图中查看/滚动到<Item2/>,那么我应该突出显示选项卡中的item2图标。

此外,如果用户点击标签项,它会自动使滚动视图滚动到相应项目的部分。

有没有办法在react native中实现这个scrollspy功能?

1 个答案:

答案 0 :(得分:4)

要实现理想的行为,您需要结合几件事。

  1. ScrollView有一个名为scrollTo的方法,您可以滚动到所需的内容位置。
  2. 为了能够检测ScrollView当前位置是什么,您需要使用onScroll属性。您可以将此属性与scrollEventThrottle结合使用,以获得更高的准确性。
  3. 要获得每个项目在ScrollView内的位置,您需要为每个项目实施onLayout属性。
  4. 以下是一个小样本应用,展示了如何实施上述步骤。

    const Item = props => (
      <View
        style={{ minHeight: 500 }}
        onLayout={e => props.onItemLayout(e, props.index)}>
        <Text>{`Item ${props.index}`}</Text>
      </View>
    );
    
    export default class App extends Component {
      constructor() {
        super();
        this.state = {
          sections: [1, 2, 3],
          currentSection: 1
        };
      }
      moveToSetion = section => {
        // scroll view to section
        this.scrollView.scrollTo({ x: 0, y: this.state[section], animated: true });
        // set state to current section
        this.setState({ currentSection: section });
      };
      onItemLayout = ({ nativeEvent: { layout: { x, y, width, height } } }, section) => {
        // setting each items position
        this.setState({ [section]: y });
      };
      onScroll = ({ nativeEvent: { contentOffset: { y, x } } }) => {
        let _currentSection;
        // loop sections to calculate which section scrollview is on
        this.state.sections.forEach((section) => {
          // adding 15 to calculate Text's height
          if((y + 15) > this.state[section]) _currentSection = section
        })
        // settint the currentSection to the calculated current section
        this.setState({ currentSection: _currentSection })
      }
      render() {
        return (
          <View style={styles.container}>
            <View style={styles.buttonContainer}>
              {this.state.sections.map(section => (
                <Button
                  key={section}
                  title={`Section ${section}`}
                  color={this.state.currentSection === section ? 'red' : 'blue'}
                  onPress={() => this.moveToSetion(section)}
                />
              ))}
            </View>
            <ScrollView
              style={styles.scrollView}
              ref={ref => (this.scrollView = ref)}
              scrollEventThrottle={100}
              onScroll={this.onScroll}>
              {this.state.sections.map(section => (
                <Item
                  key={section}
                  index={section}
                  onItemLayout={this.onItemLayout}
                />
              ))}
            </ScrollView>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        paddingTop: Constants.statusBarHeight,
        backgroundColor: '#ecf0f1',
      },
      buttonContainer: {
        flexDirection: 'row',
        justifyContent: 'center',
        position: 'fixed',
        top: 0,
      },
      scrollView: {
        paddingLeft: 15,
        paddingRight: 15
      }
    });