在 React-Native 内滚动调用函数一次

Call function on scroll once in React-Native

我正在构建一个应用程序,其中我有一个名为 <SearchBar /> 的可实现 header,它具有用于打开和关闭容器的道具 isCollaped(值可以是 true 或 false) , 和一个 <ScrollView /> (下面的截图,很抱歉没有英文文本)。 应用截图:

Case when isCollaped == false

Case when isCollaped == true

我想在开始滚动 <ScrollView /> 时折叠 <SearchBar />,所以我将 isCollaped 的值设置为 true,它起作用了,但是因为 <ScrollView /> 每 200 毫秒调用一次 onScroll,松开手指后滚动动画不会停止,当我尝试通过按下它再次打开 <SearchBar /> 时,如果滚动动画仍然处于活动状态,我是得到一个真正有问题的动画,再次折叠 <SearchBar />

这部分代码会在按下 SearchBar 时调整它的大小:

collapseSearchBar = () => {
   this.setState({
      searchBarCollapsed: !this.state.searchBarCollapsed,
   });
};

这是<SearchBar/>:

<SearchBar
   isCollapsed={this.state.searchBarCollapsed}
   onCollapse={this.collapseSearchBar}
   onSearch={this.search}
/>

这是 <ScrollView /> 的代码:

<ListView
      ref='mainScrollView'
      dataSource={this.state.dataSource}
      renderRow={(rowData) => {
          return this._renderRow(rowData, this.props.navigation);
      }}
      onScroll={() => {
        if (!this.state.searchBarCollapsed) {
          this.setState({
            searchBarCollapsed: true
          });
        }
      }}
/>

我的问题是:当用户滑动它时,我怎样才能只调用一次 <ScrollView /> 调用 onScroll,而不是一直(每 200 毫秒,动画开始后大约 3 秒) ), 当动画处于活动状态时。

您可以使用 scrollEventThrottle 属性来设置 scroll 事件的触发频率。

scrollEventThrottle

This controls how often the scroll event will be fired while scrolling (as a time interval in ms). A lower number yields better accuracy for code that is tracking the scroll position, but can lead to scroll performance problems due to the volume of information being send over the bridge. You will not notice a difference between values set between 1-16 as the JS run loop is synced to the screen refresh rate. If you do not need precise scroll position tracking, set this value higher to limit the information being sent across the bridge. The default value is zero, which results in the scroll event being sent only once each time the view is scrolled.

但我认为这不能解决您的问题。你可以做什么来设置第二个状态值(类似于isAnimating)并在动画化header之前检查它。动画结束后,您可以将其设置回 false。

例子

<ListView
      ref='mainScrollView'
      dataSource={this.state.dataSource}
      renderRow={(rowData) => {
          return this._renderRow(rowData, this.props.navigation);
      }}
      onScroll={() => {
        if (!this.state.searchBarCollapsed) {
          this.setState({
            searchBarCollapsed: true,
            isAnimating: true
          });
        }
      }}
/>

collapseSearchBar = () => {
   this.interval = setInterval(() => {
     if (this.state.isAnimating === false) {
       this.setState({
         searchBarCollapsed: !this.state.searchBarCollapsed,
         isAnimating: true
       });
       clearInterval(this.interval);
       this.interval = null;
     }
   }, 100)
};

在我的例子中,我必须在向上滚动一次和向下滚动一次时为视图设置动画。基本上,它就像向下滚动时显示视图,向上滚动时隐藏视图。由于函数在滚动时被多次调用,因此它在视图中产生了闪烁效果。所以我创建了一个状态“scrollDown”。

const [scrollDown, setScrollDown] = useState(true)

在我的例子中,最初 scrollDown 为真,表示用户首先会向下滚动。动画将执行,因为它是 true,它也会将自身更新为 false,因此下一次动画将不会执行。当然,!scrollDown 会给出 true 但这些代码不会执行,因为它们是向上滚动的。因此,一旦用户向上滚动,scrollDown 值就会更新,并遵循相同的逻辑。

const _toogleNavigationBtn = (event) => {
    const currentOffset = event.nativeEvent.contentOffset.y;

    if(scrollDown)
        if(currentOffset > offset && offset > 0 ){
                setScrollDown(prevState => !prevState)
                Animated.timing(navigationBtnY,{
                        toValue: 50,
                        duration: 700,
                        easing: Easing.out(Easing.exp)
                    }).start();
        }

    const contentSize = parseInt(event.nativeEvent.contentSize.height)
    const scrolledSize = parseInt(currentOffset + event.nativeEvent.layoutMeasurement.height)
    if(!scrollDown)
        if(offset === 0 || currentOffset < offset || contentSize == scrolledSize){
            setScrollDown(prevState => !prevState)
            Animated.timing(navigationBtnY,{
                        toValue: 0,
                        duration: 700,
                        easing: Easing.out(Easing.exp)
                    }).start();
        }


    setOffset(prevState => currentOffset)
}