如何让我的 ScrollView 与 React Native 中的滚动条自定义指示器同步移动?

问题描述

enter image description here

所以我试图让它像滚动条浏览器一样工作,并让 ScrollView自定义指示器同步移动。现在我在 onPanResponderRelease调用scrollTo(),就像这样......

onPanResponderRelease: () => {
        pan.flattenOffset();
        scrollRef.current.scrollTo({x: 0,y:animatedScrollViewInterpolateY.__getValue(),animated: true,});

然而,为了获得我想要的效果,我相信 scrollTo() 需要从 onPanResponderMove调用,当前设置为这个...

onPanResponderMove: Animated.event([null,{ dx: pan.x,dy: pan.y }],{useNativeDriver: false})

您在此处看到的 Animated.event 是驱动指示器在触摸时移动的原因。 但是我怎样才能从 onPanResponderMove调用 scrollTo() 呢?或者这甚至是一个好主意?如果我能够做到这一点,ScrollView 将与指示器同步移动。至少我是这么认为的...

归根结底,我想要的是一个 ScrollView,它有一个滚动条,就像浏览器的滚动条一样。这意味着滚动条指示器是可触摸和可拖动的。理想情况下,我希望在 FlatList 上执行此操作,但我认为首先在 ScrollView 上执行此操作会更简单,然后我可以将相同的原则应用于 FlatList 组件之后。

这是所有代码...

import React,{ useRef,useState } from "react";
import {
  Animated,View,StyleSheet,PanResponder,Dimensions,Text,ScrollView,TouchableWithoutFeedback,} from "react-native";

const BoxSize = { width: 50,height: 50 };

const App = () => {
  const AnimatedTouchable = Animated.createAnimatedComponent(
    TouchableWithoutFeedback
  );
  const { width,height } = Dimensions.get("window");
  const pan = useRef(new Animated.ValueXY({ x: 0,y: 50 })).current;
  const scrollRef = useRef();
  const [scrollHeight,setScrollHeight] = useState(0);

  const panResponder = useRef(
    PanResponder.create({
      onMoveShouldSetPanResponder: () => true,onPanResponderGrant: () => {
        pan.setoffset({
          x: pan.x._value,y: pan.y._value,});
      },onPanResponderMove: Animated.event([null,{
        useNativeDriver: false,}),onPanResponderRelease: () => {
        pan.flattenOffset();
        scrollRef.current.scrollTo({
          x: 0,y: animatedScrollViewInterpolateY.__getValue(),})
  ).current;

  function scrollLayout(width,height) {
    setScrollHeight(height);
  }

  const animatedInterpolateX = pan.x.interpolate({
    inputRange: [0,width - BoxSize.width],outputRange: [0,extrapolate: "clamp",});

  const animatedInterpolateY = pan.y.interpolate({
    inputRange: [0,height - BoxSize.height],outputRange: [height * 0.2,height * 0.8],});
  const animatedScrollViewInterpolateY = animatedInterpolateY.interpolate({
    inputRange: [height * 0.2,411],// extrapolate: "clamp",});

  return (
    <View style={styles.container}>
      <Animated.View style={{ backgroundColor: "red" }}>
        <ScrollView
          ref={scrollRef}
          onContentSizeChange={scrollLayout}
          scrollEnabled={false}
        >
          <Text style={styles.titleText}>
            <Text>Twenty to Last!</Text>
            <Text>Ninete to Last!</Text>
            <Text>Eight to Last!</Text>
            <Text>Sevent to Last!</Text>
            <Text>Sixtee to Last!</Text>
            <Text>Fiftee to Last!</Text>
            <Text>Fourte to Last!</Text>
            <Text>Thirte to Last!</Text>
            <Text>Twelet to Last!</Text>
            <Text>Eleveh to Last!</Text>
            <Text>Tenth to Last!</Text>
            <Text>Nineth to Last!</Text>
            <Text>Eighth to Last!</Text>
            <Text>Seventh to Last!</Text>
            <Text>Sixth to Last!</Text>
            <Text>Fifth to Last!</Text>
            <Text>Fourth to Last!</Text>
            <Text>Third to Last!</Text>
            <Text>Second to Last!</Text>
            <Text>The End!</Text>
          </Text>
        </ScrollView>
      </Animated.View>
      <Animated.View
        style={{position: "absolute",transform: [
            { translateX: width - BoxSize.width },{ translateY: animatedInterpolateY },],}}
        {...panResponder.panHandlers}>
        <View style={styles.Box} />
      </Animated.View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,},titleText: {
    fontSize: 54,fontWeight: "bold",Box: {
    height: BoxSize.height,width: BoxSize.width,backgroundColor: "blue",borderRadius: 5,});

export default App;

解决方法

我首先要说的是,无论您的用例如何,将 Web 样式的滚动带到移动设备都可能是一种糟糕的用户体验。整个视图通常是可滚动的,这样你就可以从任何地方滚动而无需握住滚动条(也想想惯用左手的人)。

话虽如此,您可以将 onPanResponderMove 更改为常规回调,以便您更好地控制所需内容:

// 1) Transform Animated.event(...) to a simple callback
onPanResponderMove: (event,gestureState) => {
  pan.x.setValue(gestureState.dx);
  pan.y.setValue(gestureState.dy);

  // 2) Scroll synchronously,without animation (..because it's synchronous!)
  scrollRef.current.scrollTo({
    x: 0,y: animatedScrollViewInterpolateY.__getValue(),animated: false,});
},onPanResponderRelease: () => {
  pan.flattenOffset();
  // 3) Remove scrollTo from the release event
},