Some Examples » 17. Page: Indicators

17. Page: Indicators

(You can also tap the indicators to directly go to a page.)

Code component

We use React’s useState() hook to keep track of the current page.

const pages = [1, 2, 3, 4, 5]
const indicatorSize = 10
const indicatorPadding = 10
const indicatorWidth = pages.length * indicatorSize
const indicatorPaddingTotal = (pages.length - 1) * indicatorPadding
const indicatorWidthTotal = indicatorWidth + indicatorPaddingTotal
const indicatorAlpha = 0.3

export default function CC_17_Page_Indicators(props) {
    const [current, setCurrent] = useState(0)

    return (
        <div
            style={{
                width: 400,
                height: 400,
                ...props.style,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
            }}
        >
            <Page
                width={150}
                height={150}
                radius={30}
                currentPage={current}
                onChangePage={(current, previous) => setCurrent(current)}
            >
                {pages.map((index) => {
                    return (
                        <div
                            style={{
                                width: 150,
                                height: 150,
                                borderRadius: 30,
                                backgroundColor: "#fff",
                            }}
                        />
                    )
                })}
            </Page>

            {pages.map((index) => {
                return (
                    <motion.div
                        style={{
                            width: indicatorSize,
                            height: indicatorSize,
                            borderRadius: "50%",
                            backgroundColor: "#fff",
                            position: "absolute",
                            top: "calc(50% + 100px)",
                            left: `calc(50% + ${index - 1} * ${
                                indicatorSize + indicatorPadding
                            }px)`,
                            x: -indicatorWidthTotal / 2,
                        }}
                        animate={{
                            opacity: current === index - 1 ? 1 : indicatorAlpha,
                        }}
                        onTap={() => setCurrent(index - 1)}
                    />
                )
            })}
        </div>
    )
}

Pro tip: When using the <Page> component in a React project (like in the CodeSandbox above), set the box-sizing of all elements to border-box. Like this, for instance:

* {
    box-sizing: border-box;
}

<Page>
box-sizing, CSS universal selector (*)

Code overrides

Here I used Framer’s createStore() to communicate between the overrides.

The Indicators() override is attached to the parent frame of the indicators. It maps through its children (the five indicators) and clones them, giving them each an animate to the correct opacity and an onTap() event so that you can tap them to change the page.

const useStore = createStore({ currentIndicator: 0 })

export function Indicators(Component): ComponentType<any> {
    return (props) => {
        const { children, ...rest } = props

        const [store, setStore] = useStore()

        return (
            <Component
                {...rest}
                children={children.map((indicator, index) => {
                    let opacity = 0.3
                    if (index === store.currentIndicator) {
                        opacity = 1
                    }

                    return cloneElement(indicator, {
                        animate: { opacity: opacity },
                        onTap: () => setStore({ currentIndicator: index }),
                    })
                })}
            />
        )
    }
}

export function Page(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                currentPage={store.currentIndicator}
                onChangePage={(current, previous) =>
                    setStore({ currentIndicator: current })
                }
            />
        )
    }
}

Join the Mighty Guides mailing list    ( ± 6 emails/year )

GDPR

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing per their Privacy Policy and Terms.



Leave a Reply