I'm implementing a modal in React Native and want to create a unique scrolling effect where the modal has rounded corners that dynamically adjust based on scroll position. Specifically, I want the modal to appear like a scrollable page with rounded corners at both ends - when viewing the top of the content, the top corners should be rounded; when viewing the bottom, the bottom corners should be rounded; and when scrolling through the middle section, the corners should be square. This creates an effect similar to viewing a rounded card through a window, where the rounded edges are only visible when that portion is in view. The goal is to maintain the visual metaphor of a rounded card while providing a clear indication of scroll position.
So presently I have something like:
<View style={{borderRadius: 8}}>
<ScrollView>
// ... some content
</ScrollView>
</View>
Which has the corners always rounded but it'd be cool if it rounded only at the top and bottom... Seems like this might be a standard effect but I haven't done much frontend previously
I suggest to make your whole modal scrollable and maintain the corner radius of the modal's body, but to answer your question to how can you make the corner radius dynamic by scrolling from top to bottom, here's my answer
ScrollView
's onScroll
here's my sample code
const TestScreen = () => {
const [borderRadiusStyle, setBorderRadiusStyle] = useState<ViewStyle>(styles.roundedTop);
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const heightBeforeRound = 0 // adjust this if you want your get your corner radius before reaching the tip of the scroll
const offsetY = event.nativeEvent.contentOffset.y;
const height = event.nativeEvent.contentSize.height
const viewHeight = event.nativeEvent.layoutMeasurement.height
if (offsetY <= heightBeforeRound) {
setBorderRadiusStyle(styles.roundedTop)
} else if (offsetY + viewHeight >= height - heightBeforeRound) {
setBorderRadiusStyle(styles.roundedBottom)
} else {
setBorderRadiusStyle(styles.square)
}
};
return (
<View style={[styles.container]}>
<ScrollView
contentContainerStyle={styles.scrollViewContent}
onScroll={handleScroll}
scrollEventThrottle={16} // Adjust throttle for smoother updates
>
{/* Add your content here */}
</View>
);
};
const cornerRadius = 8 // adjust on how much corner radius do you want
const styles = StyleSheet.create({
container: {
flex: 1
},
roundedTop: {
borderTopLeftRadius: cornerRadius,
borderTopRightRadius: cornerRadius,
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
},
roundedBottom: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
borderBottomLeftRadius: cornerRadius,
borderBottomRightRadius: cornerRadius,
},
square: {
borderRadius: 0,
},
scrollViewContent: {
padding: 16,
},
});
Here's my explanation
event.nativeEvent.contentOffset.y
to get the offset y so we know where are you scrollingheight
and viewHeight
to know if you're at the bottomNote: import those types since I use typescript, remove them if you only use javascript