Animation » Example Animations » 33. Layout animation

33. Layout animation

Give a motion element a layout property and it will automatically animate when its position (or size) changes.

Code component

The motion.divs are in a grid, which defines their actual position. What makes them change position is the fact that their keys are changed.

The code inside useEffect() runs every time the items state changes,… which will be every second because that’s when the setTimeout() inside it calls setItems() again.

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 colors = ["#f44", "#3f0", "#fb0", "#0ef"]

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

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

    return (
        <div>
            <div
                style={{
                    display: "grid",
                    gridTemplateColumns: "auto auto",
                    gridGap: 10,
                }}
            >
                {items.map((item) => (
                    <motion.div
                        style={{
                            width: 75,
                            height: 75,
                            borderRadius: 20,
                            backgroundColor: colors[item - 1],
                        }}
                        key={item}
                        layout
                        transition={{
                            type: "spring",
                            stiffness: 350,
                            damping: 25,
                        }}
                    />
                ))}
            </div>
        </div>
    )
}

Code overrides

Automatic layout animations don’t work with code overrides. I included this example simply to demonstrate this. Update: With Framer Motion 5 (not yet released, but already used inside Framer), layout animations now do work with code overrides.

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

const useStore = createStore({ position: 0 })

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

        useEffect(() => {
            setTimeout(() => setStore({ position: store.position + 1 }), 1000)
        }, [store])

        return <Component {...props} />
    }
}

export const Squares = (Component): ComponentType => {
    return (props) => {
        const { style, name, ...rest } = props

        const [store, setStore] = useStore()

        return (
            <Component
                {...rest}
                layout
                transition={{
                    type: "spring",
                    stiffness: 350,
                    damping: 25,
                }}
                style={{
                    ...style,
                    top: positions[(store.position + Number(name)) % 4].top,
                    left: positions[(store.position + Number(name)) % 4].left,
                }}
                transition={{ type: "spring", stiffness: 350, damping: 25 }}
            />
        )
    }
}

Leave a Reply