编辑:我讨厌谷歌搜索答案,并且发现一些十年前从未解决过的问题,所以我为可能想知道的人回答自己的问题。就我而言,我只是为滚动视图禁用了bounces
道具。由于FlatList扩展了React的ScrollView,因此在我创建的动画FlatList组件中将bounces
设置为false
可以阻止它弹起并解决了我的问题。祝你有美好的一天。
希望您过得愉快。我正在尝试动态设置标题的动画,但是由于某种原因,每当我滚动到滚动视图的开始或结尾之外时,反弹效果就会与Animation混淆。(如下面的gif所示)
Same GIF but higher resolution
如您所见,当我滚动到顶部并启用bounce动画时,页眉认为我正在向下滚动,因为bounce将列表中的第一个元素返回顶部。我该如何解决?我在网上某处看到,向动画值添加插值器会有所帮助,尽管我不太了解。 下面是我的代码。谢谢
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
const tempArray = [
...(an array of my data)
]
export default class TempScreen extends React.Component {
static navigationOptions = {
header: null
}
constructor(props) {
super(props)
this.state = {
animatedHeaderValue: new Animated.Value(0),
}
}
render() {
const animatedHeaderHeight = Animated.diffClamp(this.state.animatedHeaderValue, 0, 60)
.interpolate({
inputRange: [0, 70],
outputRange: [70, 0],
})
return ( <
View >
<
Animated.View style = {
{
backgroundColor: 'white',
borderBottomColor: '#DEDEDE',
borderBottomWidth: 1,
padding: 15,
width: Dimensions.get('window').width,
height: animatedHeaderHeight,
}
} >
<
/Animated.View> <
AnimatedFlatList scrollEventThrottle = {
16
}
onScroll = {
Animated.event(
[{
nativeEvent: {
contentOffset: {
y: this.state.animatedHeaderValue
}
}
}]
)
}
data = {
tempArray
}
renderItem = {
({
item
}) =>
<
View style = {
{
flex: 1
}
} >
<
Text style = {
{
fontWeight: 'bold',
fontSize: 30
}
} > {
item.name
} < /Text> <
Text > {
item.year
} < /Text> <
/View>
}
/>
<
/View>
)
}
}
答案 0 :(得分:2)
如果只想解决“弹跳”问题,则问题是iOS赋予diffClamp负的scrollY值。您需要对它们进行过滤,并确保scrollY保持> = 0,以避免diffClamp受到过度滚动的影响。
const clampedScrollY = scrollY.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
另一个不错的技巧是使用“悬崖”技术,以使标题仅在最小scrollY位置之后消失。
这是我的应用中的代码:
const minScroll = 100;
const clampedScrollY = scrollY.interpolate({
inputRange: [minScroll, minScroll + 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
const minusScrollY = Animated.multiply(clampedScrollY, -1);
const translateY = Animated.diffClamp(
minusScrollY,
-AnimatedHeaderHeight,
0,
);
const opacity = translateY.interpolate({
inputRange: [-AnimatedHeaderHeight, 0],
outputRange: [0.4, 1],
extrapolate: 'clamp',
});
clampedScrollY
将是:
您明白了。因此diffClamp
仅在scrollY> 100时才> 0,并在该阈值后以1递增1。
答案 1 :(得分:1)
我像两个小时前一样遇到了同样的问题...
您可以设置Scrollview
属性bounces=false
,但是如果您想让RefreshControl
刷新ScrollView
内容(例如我的情况),则bounce属性必须保持活动状态
我在这篇很酷的文章https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a之后修正了这个问题。
我不是Animated库的专家,所以我发布了代码:
constructor(props) {
const scrollAnim = new Animated.Value(0);
const offsetAnim = new Animated.Value(0);
this.state = {
scrollAnim,
offsetAnim,
AnimatedViewHeight: 1,
clampedScroll: Animated.diffClamp(
Animated.add(
scrollAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
}),
offsetAnim
),0, 1
)
}
}
render() {
const minScroll = this.state.AnimatedViewHeight;
const navbarTranslate = this.state.clampedScroll.interpolate({
inputRange: [0, minScroll],
outputRange: [0, -minScroll],
extrapolate: 'clamp',
});
return (
<View style={{
flex: 1
}}>
<Animated.View
onLayout={(event) => {
var { height } = event.nativeEvent.layout;
this.setState({
AnimatedViewHeight: height,
clampedScroll: Animated.diffClamp(
Animated.add(
this.state.scrollAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
}),
this.state.offsetAnim
), 0, height)
})
}}
style={[{ transform: [{ translateY: navbarTranslate }] }]}>
<View><Text>THIS IS YOUR HEADER</Text></View>
</Animated.View>
<AnimatedFlatList
// iOS offset for RefreshControl
contentInset={{
top: this.state.AnimatedViewHeight,
}}
contentOffset={{
y: -this.state.AnimatedViewHeight,
}}
scrollEventThrottle={1}
onScroll={
Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
{ useNativeDriver: true },
)}
data={this.state.data}
keyExtractor={(item, idx) => idx}
ListFooterComponent={this.renderFooter}
renderItem={this.renderItem}
onEndReached={this.handleLoadMore}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh}
// Android offset for RefreshControl
progressViewOffset={this.state.AnimatedViewHeight}
/>
}
onEndReachedThreshold={0.5} />
</View>
)
}
this.state.AnimatedViewHeight
是标题的高度,可以通过调用onLayout
函数来获取。在此函数内部,我还设置了一个新的lampedScroll,因为我有一个新的高度(在我的情况下,标头没有固定的大小)。
然后,在render()
中,根据您的Animated Scrollview的滚动位置定义一个变量(navbarTranslate
)来控制headerSize。
答案 2 :(得分:0)
我使用此答案https://stackoverflow.com/a/51638296/3639398
解决了import React from 'react';
import {
Animated,
Text,
View,
StyleSheet,
ScrollView,
Dimensions,
RefreshControl,
} from 'react-native';
import Constants from 'expo-constants';
import randomColor from 'randomcolor';
const HEADER_HEIGHT = 44 + Constants.statusBarHeight;
const BOX_SIZE = Dimensions.get('window').width / 2 - 12;
const wait = (timeout: number) => {
return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
};
function App() {
const [refreshing, setRefreshing] = React.useState(false);
const scrollAnim = new Animated.Value(0);
const minScroll = 100;
const clampedScrollY = scrollAnim.interpolate({
inputRange: [minScroll, minScroll + 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
});
const minusScrollY = Animated.multiply(clampedScrollY, -1);
const translateY = Animated.diffClamp(minusScrollY, -HEADER_HEIGHT, 0);
const onRefresh = React.useCallback(() => {
setRefreshing(true);
wait(2000).then(() => {
setRefreshing(false);
});
}, []);
return (
<View style={styles.container}>
<Animated.ScrollView
contentContainerStyle={styles.gallery}
scrollEventThrottle={1}
bounces={true}
showsVerticalScrollIndicator={false}
style={{
zIndex: 0,
height: '100%',
elevation: -1,
}}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollAnim } } }],
{ useNativeDriver: true }
)}
overScrollMode="never"
contentInset={{ top: HEADER_HEIGHT }}
contentOffset={{ y: -HEADER_HEIGHT }}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>
{Array.from({ length: 20 }, (_, i) => i).map((uri) => (
<View style={[styles.box, { backgroundColor: 'grey' }]} />
))}
</Animated.ScrollView>
<Animated.View style={[styles.header, { transform: [{ translateY }] }]}>
<Text style={styles.title}>Header</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
gallery: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: 4,
},
box: {
height: BOX_SIZE,
width: BOX_SIZE,
margin: 4,
},
header: {
flex: 1,
height: HEADER_HEIGHT,
paddingTop: Constants.statusBarHeight,
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: randomColor(),
},
title: {
fontSize: 16,
},
});
export default App;