In this tutorial we'll go through the steps of building a pager component from "scratch" (not entirely accurate -- we'll use a few dependencies to help with handling gestures and animations later on). This first article will capture the core behaviour of the pager component before moving onto things like animations and gestures.
I originally implemented this in react-native, so an example of what the final product will (sort of) look like can be found here: https://github.com/ajsmth/react-native-pager-component#readme
Note: I know that components for this kind of thing already exist -- this was made for my own learning, and hopefully yours too.
Before getting into the code, let's think about what we want to do...
We want a component that, when given a certain index, moves the matching child index into focus. So we'll be thinking in terms of indexes -- when the activeIndex
prop changes, we'll want to shift the focus to the child that matches that index:
onChange(activeIndex) -> updateTranslation(activeIndex)
We can achieve this by laying out all of our children horizontally inside a container view, like this:
const absoluteFill = {
position: "absolute",
left: 0,
right: 0,
bottom: 0,
top: 0
};
function Pager({ children, activeIndex, size }) {
return (
<div
style={{
display: "flex",
alignSelf: "center",
position: "relative",
width: size,
height: size,
border: "2px solid green"
}}
>
<div
style={{
...absoluteFill
// we will translate this container view
}}
>
{React.Children.map(children, (element, index) => (
<PageView index={index} width={size}>
{element}
</PageView>
))}
</div>
</div>
);
}
function PageView({ children, index }) {
// position each page side-by-side based on it's index
const position = `translateX(calc(${100 * index}%))`;
return (
<div
style={{
...absoluteFill,
transform: position,
border: "thin solid red"
}}
>
{children}
</div>
);
}
Now all we have to do is update the translation value of the inner container based on the activeIndex
prop:
function Pager({ children, activeIndex, size }) {
// the total offset of the container div -- based on activeIndex
const translateX = `translateX(${activeIndex * -100}%)`;
return (
<div ...>
<div
style={{
...absoluteFill,
transform: translateX
}}
>
{React.Children.map(children, (element, index) => (
<PageView index={index} width={size}>
{element}
</PageView>
))}
</div>
</div>
);
}
The inner container now shifts its focus by computing its offset from the activeIndex
prop. This is a pretty basic implementation so far, but hopefully you can see that it captures the core behaviour of what the final component will do.
The next steps will be to animate page transitions, and handle gestures to update the activeIndex
based on user interaction. We'll also open up the component's API to make it fully controllable for use in other projects.
To see a full example visit https://codesandbox.io/s/modern-moon-o5etr
In the next article, we'll animate the transitions between pages:
https://dev.to/ajsmth/building-a-pager-component-from-scratch-part-2-557l
Top comments (0)