Animation » Example Animations » 17. Page: Indicators

17. Page: Indicators

Code component

React’s useState() hook is used 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>
            <Page
                width={150}
                height={150}
                radius={30}
                center
                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,
                        }}
                        opacity={indicatorAlpha}
                        animate={{
                            opacity: current === index - 1 ? 1 : indicatorAlpha,
                        }}
                    />
                )
            })}
        </div>
    )
}

Code overrides

Here Framer’s createStore() is used to communicate between the distinct 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.

const useStore = createStore({ currentIndicator: 0 })

export function Indicators(Component): ComponentType {
    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 },
                    })
                })}
            />
        )
    }
}

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

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

Leave a Reply