如何让我的 ScrollView 与 React Native 中的 Scrollbar Custom Indicator 同步移动?
How can I get my ScrollView to move synchronously with my Scrollbar Custom Indicator in React Native?
所以我试图让它像滚动条浏览器一样工作,并让 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, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
pan.flattenOffset();
scrollRef.current.scrollTo({
x: 0,
y: animatedScrollViewInterpolateY.__getValue(),
animated: true,
});
},
})
).current;
function scrollLayout(width, height) {
setScrollHeight(height);
}
const animatedInterpolateX = pan.x.interpolate({
inputRange: [0, width - boxSize.width],
outputRange: [0, width - boxSize.width],
extrapolate: "clamp",
});
const animatedInterpolateY = pan.y.interpolate({
inputRange: [0, height - boxSize.height],
outputRange: [height * 0.2, height * 0.8],
extrapolate: "clamp",
});
const animatedScrollViewInterpolateY = animatedInterpolateY.interpolate({
inputRange: [height * 0.2, height * 0.8],
outputRange: [0, 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;
首先我要说的是,无论您的用例如何,将网络风格的滚动带到移动设备上都可能是糟糕的用户体验。整个视图通常是可滚动的,因此您可以从任何地方滚动而无需按住滚动条(也想想惯用左手的人)。
话虽如此,您可以将 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
},
所以我试图让它像滚动条浏览器一样工作,并让 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, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
pan.flattenOffset();
scrollRef.current.scrollTo({
x: 0,
y: animatedScrollViewInterpolateY.__getValue(),
animated: true,
});
},
})
).current;
function scrollLayout(width, height) {
setScrollHeight(height);
}
const animatedInterpolateX = pan.x.interpolate({
inputRange: [0, width - boxSize.width],
outputRange: [0, width - boxSize.width],
extrapolate: "clamp",
});
const animatedInterpolateY = pan.y.interpolate({
inputRange: [0, height - boxSize.height],
outputRange: [height * 0.2, height * 0.8],
extrapolate: "clamp",
});
const animatedScrollViewInterpolateY = animatedInterpolateY.interpolate({
inputRange: [height * 0.2, height * 0.8],
outputRange: [0, 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;
首先我要说的是,无论您的用例如何,将网络风格的滚动带到移动设备上都可能是糟糕的用户体验。整个视图通常是可滚动的,因此您可以从任何地方滚动而无需按住滚动条(也想想惯用左手的人)。
话虽如此,您可以将 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
},