UIcraft / Craft

Craft / Animation

Waving Flag

Cut one flag into columns, stagger the motion, and make a flat image behave like cloth.

Demo

Interactive
Waving flag24 columns

This is the finished component. Copy both files, keep the SVG in public, and change the three constants to tune the cloth.

waving-flag.tsx
import type { CSSProperties } from "react"
 
const COLUMNS = 24
const BILLOW = 0.8
const STAGGER = 50
 
export function WavingFlag() {
  const firstDelay = -COLUMNS * STAGGER
 
  return (
    <span
      className="waving-flag"
      style={{ "--columns": COLUMNS } as CSSProperties}
      role="img"
      aria-label="Flag of India"
    >
      {Array.from({ length: COLUMNS }, (_, index) => (
        <span
          key={index}
          className="waving-flag__column"
          style={{
            "--billow": `${index * BILLOW}px`,
            animationDelay: `${firstDelay + index * STAGGER}ms`,
          } as CSSProperties}
        >
          <img
            src="/india-flag.svg"
            alt=""
            className="waving-flag__slice"
            style={{ left: `${-index * 100}%` }}
          />
        </span>
      ))}
    </span>
  )
}
globals.css
.waving-flag { display: inline-flex; width: 192px; height: 128px; }
.waving-flag__column { position: relative; flex: 1 0 0; overflow: hidden; }
.waving-flag__slice {
  position: absolute;
  top: 0;
  height: 100%;
  width: calc(var(--columns) * 100%);
  max-width: none;
}
.waving-flag__column {
  animation: wave 500ms ease-in-out infinite alternate backwards;
}
@keyframes wave {
  from { transform: translateY(var(--billow)); }
  to { transform: translateY(calc(var(--billow) * -1)); }
}

The component has no runtime dependency. Use any rectangular image; the technique is not specific to a flag.