RN 触底展示评论框效果

想实现一个触底显示评论框的效果,参考了下手机版Safari浏览器的效果,当网页滑动到底部,就出现输入框,离开就消失。看起来简单,做起来也遇到了些问题。

一开始的想法是,评论框就在webview下方,只是被超出不展示,滑动到底部后,评论框自动滑出,离开自动滑入。

但这个简单的效果,在webview里却不好实现。webview 里面的网页滑动,无法传递到外面的容器,也就是到底后外面的View 并不知道。于是想到,给页面注入js ,或者监听webview 的 onscroll 事件,触底后,在滑动外面的容器。

为了外面容器能滑动,尝试了scrollView 和Flatlist , 但遗憾的是,这两个滑动容器,会和webview 滑动冲突,也就是说,容器能滑动,里面页面就不能滑动;里面页面能滑动,容器就不能滑动。好在有enableScroll 属性,通过监听是否触底,来开启和关闭,让同一时间只有一个容器能滑动。但效果也差强人意,比如里面的快速滑动到底部,触底后外面滑动,两段滑动效果很生硬。

  <>
      <View style={styles.container}>
        <Header onBack={handleBack} title={'222s'} />
        <FlatList
          style={{height: Dimensions.get('window').height}}
          data={[{key: 'webview'}, {key: 'commentBox'}]}
          ref={flatListRef}
          onViewableItemsChanged={onViewableItemsChanged}
          viewabilityConfig={viewabilityConfig}
          renderItem={({item}) => {
            if (item.key === 'webview') {
              return renderWebView();
            } else if (item.key === 'commentBox') {
              return renderCommentBox();
            }
            return null;
          }}
          // ListFooterComponent={renderCommentBox()} // 始终渲染评论框
          keyExtractor={item => item.key}
          scrollEnabled={isScrollEnabled}
          nestedScrollEnabled={true}
          // scrollEnabled={isScrollEnabled} // 在评论框显示时禁用外部滚动
        />
      </View>
    </>

最终还是放弃了这个方案,换了一个思路,改用flex 和动画来实现。

思路是在一个flex容器中,webview 先充满屏幕100%,评论框在底部,高度为0;当里面滑动到底部的时候,评论框高度为100,压缩webview 空间,显得评论框从底部出来了,并且有一个高度变化的动画。整体效果比之前的方案好。

<>
      <View style={styles.container}>
        <Header onBack={handleBack} title={'222s'} />
        <View style={styles.webview}>
          <Webview
            // style={[showCommentBox ? styles.webviewShifted : null]}
            url={url}
            ref={webview}
            onScroll={event => {
              const {contentOffset, layoutMeasurement, contentSize} =
                event.nativeEvent;
              const currentScrollY = contentOffset.y;
              // 判断滚动方向
              if (currentScrollY > lastScrollY) {
                // 向下滚动
                const isAtBottom =
                  event.nativeEvent.layoutMeasurement.height +
                    contentOffset.y >=
                  event.nativeEvent.contentSize.height - 10;
                if (isAtBottom) {
                  console.log('到底了');
                  setShowCommentBox(false); // 隐藏评论框
                  // 向下滚动时,显示评论框
                  Animated.timing(commentBoxHeight, {
                    toValue: 100, // 显示评论框
                    duration: 300,
                    useNativeDriver: false,
                  }).start();
                }
              } else if (currentScrollY < lastScrollY) {
                // 向上滚动
                const isAtBottom =
                  event.nativeEvent.layoutMeasurement.height +
                    contentOffset.y >=
                  event.nativeEvent.contentSize.height - 10;
                const isNearBottom =
                  layoutMeasurement.height + currentScrollY >=
                  contentSize.height - 500;

                if (isNearBottom) {
                  console.log('离开底了');
                  setShowCommentBox(true); // 显示评论框
                  // 向上滚动时,隐藏评论框
                  Animated.timing(commentBoxHeight, {
                    toValue: 0, // 隐藏评论框的高度
                    duration: 300,
                    useNativeDriver: false,
                  }).start();
                }
              }

              setLastScrollY(currentScrollY);
            }}
          />
        </View>
        <Animated.View style={[styles.commentBox, {height: commentBoxHeight}]}>
          <Input
            style={styles.input}
            placeholder="写下你的..."
            value={comment}
            onChangeText={setComment}
          />
          <Button
            title="提交"
            onPress={() => {
              setComment('');
            }}
          />
        </Animated.View>
      </View>
    </>

Leave a Comment

邮箱地址不会被公开。 必填项已用*标注