15. 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 function CC15PageIndicators() {
const [current, setCurrent] = useState(0)
return (
<div>
<Page
// Visual & layout
width={150}
height={150}
radius={30}
center
// Animation
onChangePage={(current, previous) => {
setCurrent(current)
}}
>
{pages.map(index => {
return (
<Frame
// Visual & layout
size={150}
radius={30}
backgroundColor="#fff"
// Required by React
key={index}
/>
)
})}
</Page>
{pages.map(index => {
return (
<Frame
// Visual & layout
size={indicatorSize}
radius={30}
backgroundColor="#fff"
top="calc(50% + 100px)"
style={{
left: `calc(50% + ${index - 1} * ${indicatorSize +
indicatorPadding}px)`,
}}
x={-indicatorWidthTotal / 2}
// Animation
opacity={indicatorAlpha}
animate={{
opacity: current === index - 1 ? 1 : indicatorAlpha,
}}
// Required by React
key={index}
/>
)
})}
</div>
)
}
Framer Motion
Not possible, because there’s no page component in Framer Motion library.
Override
Here Framer’s data object 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 appState = Data({
currentIndicator: 0,
})
export function Indicators(props): Override {
return {
children: props.children.map((indicator, index) => {
let opacity = 0.3
if (index === appState.currentIndicator) {
opacity = 1
}
return cloneElement(indicator, {
animate: { opacity: opacity },
})
}),
}
}
export function Page(): Override {
return {
onChangePage(current, previous) {
appState.currentIndicator = current
},
}
}