Skip to content

Transforms

Ripl supports element-level transformations — translate, scale, and rotate — that apply to any element or group. Transforms are set as properties on the element and are automatically applied to the rendering context before the element draws.

Every element exposes translateX, translateY, transformScaleX, transformScaleY, rotation, transformOriginX, and transformOriginY properties. These work identically across Canvas and SVG contexts and are fully animatable with smooth interpolation.

Demo

Rotation

Rotation can be specified as a number (radians) or a string with a unit suffix:

ts
// Radians (number)
rect.rotation = Math.PI / 4;

// Degrees (string)
rect.rotation = '45deg';

// Radians (string)
rect.rotation = '0.785rad';

When no unit suffix is provided on a string value, it is treated as radians.

Transform Origin

Transform origin defines the pivot point for rotation and scale. It can be a number (pixels) or a percentage string relative to the element's bounding box:

ts
// Pixel values
rect.transformOriginX = 50;
rect.transformOriginY = 50;

// Percentage of element dimensions
rect.transformOriginX = '50%';
rect.transformOriginY = '50%';

When set to '50%', the element rotates and scales around its center. The percentage is resolved relative to the element's width and height properties.

Application Order

Transforms are applied to the rendering context in the following order:

  1. Translate to the origin point (transformOriginX, transformOriginY)
  2. Translate by translateX, translateY
  3. Rotate by rotation
  4. Scale by transformScaleX, transformScaleY
  5. Translate back from the origin point

This order ensures that rotation and scaling happen around the specified origin.

Group Inheritance

When transforms are applied to a group, all children inherit the transformation. The context is saved before the group renders and restored afterward, so transforms compose naturally:

ts
import {
    createCircle,
    createGroup,
    createRect,
} from '@ripl/web';

const group = createGroup({
    children: [
        createRect({
            x: 0,
            y: 0,
            width: 80,
            height: 80,
            fill: '#3a86ff',
        }),
        createCircle({
            cx: 120,
            cy: 40,
            radius: 30,
            fill: '#ff006e',
        }),
    ],
});

// Both children will be translated and rotated together
group.translateX = 100;
group.translateY = 50;
group.rotation = '15deg';

Animating Transforms

All transform properties are fully animatable using renderer.transition(). String values (degrees, percentages) are interpolated smoothly:

ts
// Animate rotation from 0 to 360 degrees
await renderer.transition(rect, {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        rotation: '360deg',
    },
});

// Animate translation
await renderer.transition(rect, {
    duration: 800,
    state: {
        translateX: 200,
        translateY: 100,
    },
});

// Animate scale
await renderer.transition(rect, {
    duration: 600,
    state: {
        transformScaleX: 2,
        transformScaleY: 2,
    },
});

SVG Support

Transforms work identically for both Canvas and SVG contexts. In the SVG context, transforms are serialised as SVG transform attribute values (e.g., translate(10,20) rotate(45) scale(2,2)), and the transform state is saved and restored alongside the context state stack.

NOTE

For the full list of transform properties, see the Element API Reference.