Animation » Example Animations » 34. Shuffle

34. Shuffle

Add the layout property and a frame or motion element will automatically animate when its position (or size) changes.

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 code inside useEffect() will run when 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];

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

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

  return (
    <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}
          layout
          transition={{ type: "spring", stiffness: 350, damping: 25 }}
        />
      ))}
    </div>
  );
}

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}
                    // Layout animation
                    key={item}
                    layout
                    transition={{ 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}
                    layout
                    transition={{ type: "spring", stiffness: 350, damping: 25 }}
                />
            ))}
        </Frame>
    )
}

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

Overrides

Automatic layout animations don’t seem to work with overrides.


Leave a Reply