想实现一个触底显示评论框的效果,参考了下手机版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> </>