Animate

Transforms and pivots

A target's move, turn, scale and skew become one transform. It turns, scales and skews about a point called its pivot, which can stay in one place or switch from point to point over time.

Pick a pivot

A target turns, scales and skews about its pivot. The clip's pivots gives each target one, in the units of the element its transform is written to. Without one, it is that element's origin: 0, 0.

Pick a pivot below. The tracks stay the same: only pivots changes. The dot marks the pivot, and the turn and the squash leave it where it is. The friend's group sits right in the SVG, so its pivot is in the SVG's own units.

0.0 / 2.6 s
Pivot
clip.json
{
  "duration": 2.6,
  "pivots": {
    "friend": { "x": 320, "y": 144 }
  },
  "tracks": [
    {
      "targetUid": "friend",
      "property": "scaleX",
      "keyframes": [
        { "time": 0, "value": 1, "easing": "power2.out" },
        { "time": 0.28, "value": 1.26, "easing": "power2.inOut" },
        { "time": 0.6, "value": 0.86, "easing": "power2.inOut" },
        { "time": 0.92, "value": 1 }
      ]
    },
    {
      "targetUid": "friend",
      "property": "scaleY",
      "keyframes": [
        { "time": 0, "value": 1, "easing": "power2.out" },
        { "time": 0.28, "value": 0.74, "easing": "power2.inOut" },
        { "time": 0.6, "value": 1.16, "easing": "power2.inOut" },
        { "time": 0.92, "value": 1 }
      ]
    },
    {
      "targetUid": "friend",
      "property": "rotate",
      "keyframes": [
        { "time": 0.7, "value": 0, "easing": "power2.inOut" },
        { "time": 1.9, "value": 360 }
      ]
    }
  ]
}

One matrix per target

Every frame, a target's move, turn, scale and skew go into one matrix: move(x, y) · turn · skew · stretch, the last three about the pivot. Read it from the right: it is stretched, then skewed, then turned, then moved. That order is fixed, whatever the order of the tracks.

Both skews are one skew, as in CSS skew(skewX, skewY), and it turns with the rotation. scale multiplies scaleX and scaleY. A part no track moves rests at its default.

The matrix is written as matrix(a b c d e f) in the element's transform attribute, in place of its own, and destroy() puts its own back. So animate a group around the art, and keep the art's own transforms inside it.

One exception: the outermost <svg> is a box in the page. Its matrix goes in its style, and its pivot is a share of its box (0.5, 0.5 is its middle), written as its transform-origin. An anchor track on it is not kept from jumping: the shift a switch carries is left out there.

The transform properties
xnumberdefault 0
Moves it across, in user units.
ynumberdefault 0
Moves it down, in user units. Up is negative.
rotatenumberdefault 0
Turns it, in degrees. Positive is clockwise.
scalenumberdefault 1
Stretches it both ways: multiplies scaleX and scaleY.
scaleXnumberdefault 1
Stretches it across. 1 is its own size.
scaleYnumberdefault 1
Stretches it up and down.
skewXnumberdefault 0
Slants it along x, in degrees.
skewYnumberdefault 0
Slants it along y, in degrees.

Keyed pivots

An anchor track moves the pivot over time. Each keyframe is two fractions of the target's own box, written "ax ay": "0 0" is its left top, "1 1" its right bottom. The box is the target's entry in the clip's boxes.

A switch is a step: from its keyframe on, the target turns about the new point. The target itself does not jump when it switches. Each switch carries the shift that keeps the matrix exactly what it was at that moment, worked out once when the clip is compiled. So a square that tips over one corner, then the next, rolls along.

The dot is the point it turns about, with the keyframe holding under it. Pick "one pivot" to play the same turn about its first corner the whole way: it swings down through the ground.

0.0 / 5.0 s
Pivot
clip.json
{
  "duration": 5,
  "boxes": {
    "square": { "x": 120, "y": 120, "width": 80, "height": 80 }
  },
  "tracks": [
    {
      "targetUid": "square",
      "property": "rotate",
      "keyframes": [
        { "time": 0, "value": 0 },
        { "time": 0.5, "value": 90 },
        { "time": 1, "value": 180 },
        { "time": 1.5, "value": 270 },
        { "time": 2, "value": 360 },
        { "time": 2.5, "value": 360 },
        { "time": 3, "value": 270 },
        { "time": 3.5, "value": 180 },
        { "time": 4, "value": 90 },
        { "time": 4.5, "value": 0 }
      ]
    },
    {
      "targetUid": "square",
      "property": "anchor",
      "keyframes": [
        { "time": 0, "value": "1 1" },
        { "time": 0.5, "value": "1 0" },
        { "time": 1, "value": "0 0" },
        { "time": 1.5, "value": "0 1" },
        { "time": 3, "value": "0 0" },
        { "time": 3.5, "value": "1 0" },
        { "time": 4, "value": "1 1" }
      ]
    }
  ]
}
An anchor track
property: "anchor"
Keys the pivot, in place of the target's pivots entry. It needs a move, turn, scale or skew track on the same target: on its own it plays nothing, and nothing is noted in program.skipped.
valuenumber | stringrequired
Two fractions of the box in a string, "ax ay" (a comma works too). "0.5 0.5" is its middle. A value it can't read, such as a lone number, is left out and noted in program.skipped.
timenumberrequired
Seconds: from here on, it turns about this point. The first keyframe holds from the start.
easingstringoptional
Not read: a switch is a step.

Several players on one element

Players and pointer actions that share a stage can move the same element. The stage keeps one transform for it and adds them up: moves add, turns add, skews add, stretches multiply. Exclusive drawers are the exception: of the drawers made with svgDrawer's exclusive option on one element, each part (move x, move y, turn, skew, stretch) comes from the last of them to draw that part. What is not exclusive still adds.

Turn, skew and stretch each happen about one pivot: that of the last one to join that moves that part and, when it last drew, had it away from rest (a turn or skew other than 0, a stretch other than 1). A clip still on its first frame, unturned, has no say. One joined through ranked(stage, rank) with a higher rank counts as later, however late the other joined. So a bounce can stretch on its bottom edge while a spin turns about the middle.

The Clocks and the stage page shows a shared stage at work.

ranked(stage, rank)Stage
The same stage, for sources of rank rank: each one joined through it comes after every source ranked lower, so its pivot wins. Sources joined straight on the stage have rank 0.

Reference

StewDoc: pivots and boxes
pivotsRecord<string, StewPoint>default 0, 0
Where each target turns, scales and skews about, in the units of the element its transform is written to (for the outermost <svg>, a share of its box).
boxesRecord<string, StewBox>optional
Each target's own box, for its anchor track. Without one, the track is left out and noted in program.skipped.
StewPoint: a pivot
xnumberrequired
Across.
ynumberrequired
Down.
StewBox: a target's own box
x, ynumberrequired
Its left top corner, in its own units.
width, heightnumberrequired
Its size, in the same units.
matrix[number, number, number, number, number, number]optional
(a, b, c, d, e, f) from the box's units into the units of the element its transform is written to. Without it, they are the same units.
NextPaint and shape