34. Shuffle

This is an example of positionTransition, which is a way to animate a Div’s (or Frame’s) position automatically.

Open Framer Motion version in CodeSandbox

Framer Motion

Here the Divs are placed in a grid, so their actual position is defined by the grid. What makes them move around is the fact that their keys are changed.

(The useEffect() will run whenever items changes, which will be every second because after the setTimeout(), the setItems() function is called.)

const itemsA = [1, 2, 3, 4]
const itemsB = [3, 1, 4, 2]
const itemsC = [4, 3, 2, 1]
const itemsD = [2, 4, 1, 3]

const spring = {
    type: "spring",
    stiffness: 350,
    damping: 25,
}

export function FM34Shuffle() {
    const [items, setItems] = useCycle(itemsA, itemsB, itemsC, itemsD)

    React.useEffect(() => {
        setTimeout(() => setItems(), 1000)
    }, [items])

    return (
        <Center>
            <div
                style={{
                    display: "grid",
                    gridTemplateColumns: "auto auto",
                    gridGap: 10,
                }}
            >
                {items.map(item => (
                    <motion.div
                        style={{
                            width: 75,
                            height: 75,
                            borderRadius: 20,
                            backgroundColor: "#fff",
                        }}
                        key={item}
                        positionTransition={spring}
                    />
                ))}
            </div>
        </Center>
    )
}

Code Component

This is a more straightforward example: the Frames maintain the same key, but their position changes (top and left).

const items = [0, 1, 2, 3]

const positions = [
    { top: 0, left: 0 },
    { top: 0, left: 85 },
    { top: 85, left: 85 },
    { top: 85, left: 0 },
]

export function CC34Shuffle() {
    const [position, setPosition] = React.useState(0)

    React.useEffect(() => {
        setTimeout(() => setPosition(position + 1), 1000)
    }, [position])

    return (
        <Frame size={160} backgroundColor="transparent" center>
            {items.map(item => (
                <Frame
                    // Visual
                    size={75}
                    radius={20}
                    backgroundColor="#fff"
                    // Position
                    top={positions[(position + item) % 4].top}
                    left={positions[(position + item) % 4].left}
                    // Position transition
                    key={item}
                    positionTransition={{
                        type: "spring",
                        stiffness: 350,
                        damping: 25,
                    }}
                />
            ))}
        </Frame>
    )
}

Alternatively, you could turn the parent Frame into a CSS Grid and cycle the keys, as in the Framer Motion version above.

import * as React from "react"
import { Frame, useCycle } from "framer"

const itemsA = [1, 2, 3, 4]
const itemsB = [3, 1, 4, 2]
const itemsC = [4, 3, 2, 1]
const itemsD = [2, 4, 1, 3]

export function CC34Shuffle() {
    const [items, setItems] = useCycle(itemsA, itemsB, itemsC, itemsD)

    React.useEffect(() => {
        setTimeout(() => setItems(), 1000)
    }, [items])

    return (
        <Frame
            size={160}
            backgroundColor="transparent"
            center
            style={{
                display: "grid",
                gridTemplateColumns: "auto auto",
                gridGap: 10,
            }}
        >
            {items.map(item => (
                <Frame
                    // Visual
                    size={75}
                    radius={20}
                    backgroundColor="#fff"
                    // Position transition
                    position="relative"
                    key={item}
                    positionTransition={{
                        type: "spring",
                        stiffness: 350,
                        damping: 25,
                    }}
                />
            ))}
        </Frame>
    )
}

Remember that a Frame will have a default position of "absolute". That doesn’t work in a Grid, so you need to change it to "relative".

Overrides

This version doesn’t need an useEffect() because all overrides will run whenever a value in the Data object changes.

const positions = [
    { top: 0, left: 0 },
    { top: 0, left: 85 },
    { top: 85, left: 85 },
    { top: 85, left: 0 },
]

const appState = Data({
    position: 0,
})

export function Container(): Override {
    setTimeout(() => {
        appState.position = appState.position + 1
    }, 1000)

    return {}
}

export function Squares(props): Override {
    return {
        top: positions[(appState.position + Number(props.name)) % 4].top,
        left: positions[(appState.position + Number(props.name)) % 4].left,
        key: props.name,
        positionTransition: {
            type: "spring",
            stiffness: 350,
            damping: 25,
        },
    }
}

Leave a Reply