Transforms & Composition
Every primitive in Elucim accepts the same set of spatial and animation props. This uniform model means you learn the pattern once and apply it everywhere.
The Prop Model
Section titled “The Prop Model”Every element accepts three categories of props:
| Category | Controls | Examples |
|---|---|---|
| Primitive | What to draw | cx, cy, r, fill, stroke |
| Spatial | Where / how | rotation, scale, translate |
| Animation | When | fadeIn, fadeOut, draw, easing |
These compose freely on a single element:
<Circle cx={200} cy={150} r={50} // primitive — what rotation={45} translate={[10, 0]} // spatial — where fadeIn={20} easing="easeInOut" // animation — when stroke="#6c5ce7" fill="none"/>Spatial Transforms
Section titled “Spatial Transforms”All primitives (and <Group>) accept these spatial props via the shared SpatialProps interface.
rotation
Section titled “rotation”Rotation in degrees. By default the element rotates around its own center.
<Rect x={100} y={50} width={120} height={80} rotation={30} stroke="#6c5ce7" fill="none" />rotationOrigin
Section titled “rotationOrigin”Override the pivot point with an [x, y] coordinate:
<Rect x={100} y={50} width={120} height={80} rotation={45} rotationOrigin={[100, 50]} stroke="#6c5ce7" fill="none" />A uniform number or an [sx, sy] pair. Values are unitless multipliers (1 = normal size).
<Circle cx={200} cy={150} r={40} scale={1.5} stroke="#e17055" fill="none" /><Circle cx={200} cy={150} r={40} scale={[2, 0.5]} stroke="#00b894" fill="none" />translate
Section titled “translate”An [dx, dy] pixel offset applied before rotation and scale:
<Circle cx={100} cy={100} r={30} translate={[50, 20]} stroke="#fdcb6e" fill="none" />Stacking with zIndex
Section titled “Stacking with zIndex”SVG follows the painter’s model by default — elements declared later in JSX paint on top of earlier ones. The zIndex prop overrides this:
<Player width={300} height={200} fps={30} durationInFrames={60}> {/* Declared first, but renders on top because of zIndex */} <Circle cx={130} cy={100} r={50} fill="#6c5ce7" zIndex={2} /> {/* Declared second, but renders behind */} <Rect x={100} y={60} width={100} height={80} fill="#00b894" zIndex={1} /></Player>Key rules:
- Default
zIndexis 0 - Higher values render later (on top)
- Ties preserve document order (stable sort)
- Works on children of
<Scene>and<Group>
Grouping
Section titled “Grouping”<Group> is a container that applies transforms and animations to all its children as a single unit.
<Group rotation={15} translate={[50, 0]} fadeIn={20}> <Rect x={100} y={50} width={200} height={150} stroke="#6c5ce7" fill="none" /> <Circle cx={200} cy={125} r={40} stroke="#e17055" fill="none" /></Group>What this does:
- Rotate a group → rotates everything inside
- FadeIn on a group → fades in everything together
- Groups can nest — transforms compose through the hierarchy
- Groups sort their own children by
zIndex— each group creates its own stacking context
Static vs Animated Transforms
Section titled “Static vs Animated Transforms”The spatial props described above are static — they set a fixed transform for the element’s lifetime. For animated transforms (interpolating between two states over time), use the <Transform> animation wrapper:
// Fixed 45° rotation — no animation<Circle cx={100} cy={100} r={50} rotation={45} stroke="#6c5ce7" fill="none" />// Animates from 0° to 360° over 60 frames<Transform rotate={{ from: 0, to: 360 }} duration={60}> <Circle cx={100} cy={100} r={50} stroke="#6c5ce7" fill="none" /></Transform>Use static transforms for layout positioning, and <Transform> when you need motion. See Transform & Morph for the full animation API.