Reputation: 155
Is there a way to use scrollHandler (for updating the shared value) and scrollTo together
https://snack.expo.dev/@haniq313/scrollview-with-handler-and-scrollto
Snack and code below.
Problem is when i press the Next button the scrollTo works but also triggers the scrollHandler. If i disable the scrollHandler then it works fine. but then i cant manually swipe and change values. Need to make both work. Is there someway to call scrollTo and at the same time not have the scrollHandler triggered ?
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
ScrollView,
Platform,
SafeAreaView,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
import Animated, {
Extrapolate,
interpolate,
runOnJS,
useAnimatedRef,
useAnimatedScrollHandler,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
scrollTo,
} from 'react-native-reanimated';
import { Ionicons } from '@expo/vector-icons';
const data = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
// You can import from local files
import AssetExample from './components/AssetExample';
const ITEM_HEIGHT = 25;
const ITEM_WIDTH = 200;
const VISIBLE_ITEMS = 3;
const FONT_SIZE = 16;
const SpinnerItem = (props) => {
const { idx, label, scrollY } = props;
const focusedStyle = useAnimatedStyle(() => {
const opacity = interpolate(
scrollY.value,
[(idx - 2) * ITEM_HEIGHT, (idx - 1) * ITEM_HEIGHT, idx * ITEM_HEIGHT],
[0.3, 1, 0.3],
Extrapolate.CLAMP
);
const rotate = interpolate(
scrollY.value,
[(idx - 2) * ITEM_HEIGHT, (idx - 1) * ITEM_HEIGHT, idx * ITEM_HEIGHT],
[-60, 0, 60],
Extrapolate.CLAMP
);
return {
opacity,
transform: [{ rotateX: rotate + 'deg' }],
};
}, [idx, scrollY]);
const fontStyle = useAnimatedStyle(() => {
const fontStyle = interpolate(
scrollY.value,
[(idx - 2) * ITEM_HEIGHT, (idx - 1) * ITEM_HEIGHT, idx * ITEM_HEIGHT],
[FONT_SIZE, FONT_SIZE + 8, FONT_SIZE],
Extrapolate.CLAMP
);
return {
fontSize: fontStyle,
};
}, [idx, scrollY]);
return (
<Animated.View
style={[
{
alignItems: 'center',
justifyContent: 'center',
width: ITEM_WIDTH,
height: ITEM_HEIGHT,
},
focusedStyle,
]}>
<Animated.Text
allowFontScaling={false}
adjustsFontSizeToFit={false}
style={[styles.label, fontStyle]}>
{label}
</Animated.Text>
</Animated.View>
);
};
export default function App() {
const [selectedIndex, setSelectedIndex] = useState(0);
const scrollY = useSharedValue(0);
const aref = useAnimatedRef();
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
scrollY.value = event.contentOffset.y;
runOnJS(setSelectedIndex)(Math.round(scrollY.value / ITEM_HEIGHT));
},
});
useDerivedValue(() => {
scrollTo(aref, 0, scrollY.value, true);
});
const incrementScroll = () => {
'worklet';
scrollY.value = scrollY.value + ITEM_HEIGHT;
if (scrollY.value >= (data.length - 1) * ITEM_HEIGHT) scrollY.value = 0;
runOnJS(setSelectedIndex)(Math.round(scrollY.value / ITEM_HEIGHT));
};
return (
<SafeAreaView
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{'Selected Index: ' + selectedIndex}</Text>
<View style={styles.container}>
<TouchableOpacity
onPress={() => {
incrementScroll();
}}>
<Ionicons name="ios-arrow-down-sharp" size={24} color="black" />
</TouchableOpacity>
<Animated.ScrollView
showsVerticalScrollIndicator={false}
bounces={false}
ref={aref}
decelerationRate={Platform.OS === 'ios' ? 0 : 0.98}
renderToHardwareTextureAndroid
contentContainerStyle={{
alignItems: 'center',
justifyContent: 'center',
}}
centerContent
pinchGestureEnabled={false}
snapToInterval={ITEM_HEIGHT}
snapToAlignment="start"
onScroll={scrollHandler}
scrollEventThrottle={16}>
{['spacer', ...data, 'spacer'].map((item, idx) => (
<SpinnerItem
key={idx}
idx={idx}
scrollY={scrollY}
label={item === 'spacer' ? '' : item}
/>
))}
</Animated.ScrollView>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
backgroundColor: '#ecf0f1',
height: ITEM_HEIGHT * 3,
width: ITEM_WIDTH,
overflow: 'hidden',
},
label: {
textAlignVertical: 'center',
textAlign: 'center',
color: 'red',
fontSize: FONT_SIZE,
fontWeight: '700',
},
});
Upvotes: 0
Views: 2833
Reputation: 468
Try this one: link
You can simply add another animated variable and set it to true before scrolling and set it to false after scrolling. and in scroll handler check if the variable is true then don't execute inside of it
Upvotes: 3