ifelsething logoifelsething

Stop Scrolling at Irregular Intervals in React Native

Published On: 2024-07-12
Posted By: Harish

Stop Scrolling at Irregular Intervals in React Native

We know the way to stop scrolling at regular intervals using snapToInterval prop. But with this, we can only stop scrolling at multiples of a given interval and not irregular positions.

So to overcome that, we have snapToOffsets prop, which accepts an array of numbers. Here, numbers are nothing but the y-axis position (content height) for vertical scrollview and x-axis position (content width) for horizontal scrollview. For every scroll, it stops at any given nearest number.

Let'ss see this in action.

Create A New Project

Create a new react-native project by using npx. Check documentation for creating a new react native project.

npx react-native@latest init ScrollViewRN

Example Implementation

For this example, we will create a simple horizontal scrollview with color blocks. Each color block will have a width of 200 and a gap of 10. So, we will snap the scroll at different widths.

Import and add ScrollView with few scrollable color blocks.

//App.tsx
...
import { View, ScrollView } from 'react-native';
...
<ScrollView
  contentContainerStyle={styles.content_container}
  horizontal
>
  {
    colors
      .map((color: string) => {
        return (
          <View
            key={color}
            style={[
              styles.view,
              {
                backgroundColor: color
              }
            ]}
          >
          </View>
        )
      })
  }
</ScrollView>
...

If we run the app,

#for Android
npx react-native run-android

#for ios
npx react-native run-ios

We can see scrollable horizontal color blocks. If you scroll now, they will scroll freely till the momentum lasts.

Now add snapToOffsets prop with an array of values 630 and 1050.

Here, 630 is nothing but the sum of three blocks and their gaps. Each block's width is 200 and has a gap of 10. Similarly, 1050 is the sum of five color block widths.

And decelerationRate prop is used to adjust different scrolling speeds.

...
<ScrollView
  ...
  decelerationRate='fast'
  snapToOffsets={[630, 1050]}
>
...
</ScrollView>
...

Now reload the metro builder and try to scroll as fast as you can. We can see that no matter what the scrolling speed is, the scroll stops at the third block and if you continue scrolling, it again stops at the fifth block and lastly stops at the end of the content or till scroll momentum lasts.

Stop Scrolling at Irregular Intervals in React Native

This is because the content snaps at given intervals.

Snapping Effect

If you look closely, it stops at given intervals and the left edge gets snapped to that interval.

You can see this snapping effect by simple left or right swipe. If you observe the above gif, the offsets get snapped to the edge.

Snapping to Start and End

When we use snapToOffset prop, by default snapping is automatically applied to start and end of scrollview content. Means, the content scrolls back to start or end position for small pushes or pulls.

You can see from the gif below.

Stop Scrolling at Irregular Intervals in React Native

So, If you don't want that effect, we can use snapToStart and snapToEnd props to disable this feature. These props accept boolean values and default value is true for both of them.

The snapToInterval prop lacks this functionality. By default they will snap to start and end positions.

Complete code of our example,

//App.tsx
import React from "react";
import {
  Text,
  StyleSheet,
  SafeAreaView,
  StatusBar,
  View,
  ScrollView
} from "react-native";

const colors = [
  'orange',
  'green',
  'blue',
  'maroon',
  'violet',
  'darkorange',
  'gold',
  'darkgreen',
  'aquamarine',
  'cadetblue'
];

export default function App() {
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
      <StatusBar
        barStyle="dark-content"
      />
      <View style={styles.container}>
        <Text style={styles.text}>
          ifelsething.com
        </Text>
        <Text style={styles.text}>
          snap scroll to offsets
        </Text>
        <ScrollView
          horizontal
          decelerationRate={'fast'}
          snapToOffsets={[630, 1050]}
          //snapToStart={false}
          //snapToEnd={false}
          contentContainerStyle={styles.content_container}
        >
          {
            colors
              .map((color: string) => {
                return (
                  <View
                    key={color}
                    style={[
                      styles.view,
                      {
                        backgroundColor: color
                      }
                    ]}
                  >
                  </View>
                )
              })
          }
        </ScrollView>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    margin: 10,
    gap: 20
  },
  text: {
    fontSize: 15,
    color: 'black',
    fontStyle: 'italic'
  },
  content_container: {
    gap: 10
  },
  view: {
    width: 200,
    height: 200,
    borderRadius: 10
  }
});

Share is Caring

Related Posts