Skip to content

Commit

Permalink
Clarify easing handle Y coord clamping (#31)
Browse files Browse the repository at this point in the history
* Clarify easing handle Y coord clamping

Unlike their X counterpart, Y handle coordinates are not pinned to
[0 .. 1].

AE/Bodymovin can export out-of-range Ys, and the expected behavior
is for the value to extrapolate beyond the specified keyframe range.

* Update keyframe easing playground
  • Loading branch information
fmalita committed May 28, 2024
1 parent 6b009b3 commit 04f09a5
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 9 deletions.
4 changes: 4 additions & 0 deletions docs/specs/properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ The `y` axis represents the value interpolation factor, a value of 0
represents the value at the current keyframe, a value of 1 represents the
value at the next keyframe.

Unlike `x` values, `y` values are not clamped to `[0 .. 1]`. Supernormal `y`
values allow the interpolated value to overshoot (extrapolate) beyond the
specified keyframe values range.

When you use easing you have two easing handles for the keyframe:

`o` is the "out" handle, and is the first one in the bezier, determines the curve
Expand Down
18 changes: 15 additions & 3 deletions docs/static/js/value_editors.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ class BezierEditor
this.offset_x = 0;
this.offset_y = 0;
this.pad = 0;

// true: Y is clamped to [0..1]
// false: Y is unclammped, and controls can move within the padding area
this.clampY = true;
}

get width()
Expand Down Expand Up @@ -341,10 +345,14 @@ class BezierEditor
_mouse_event_pos(ev)
{
let rect = this.canvas.getBoundingClientRect();
let minX = this.pad;
let maxX = this.width - this.pad;
let minY = this.clampY ? this.pad : 0;
let maxY = this.width - (this.clampY ? this.pad : 0);

return {
x: Math.max(this.pad, Math.min(this.width - this.pad, (ev.clientX - rect.left) * this.canvas.width / rect.width)),
y: Math.max(this.pad, Math.min(this.height - this.pad, (ev.clientY - rect.top) * this.canvas.height / rect.height)),
x: Math.max(minX, Math.min(maxX, (ev.clientX - rect.left) * this.canvas.width / rect.width)),
y: Math.max(minY, Math.min(maxY, (ev.clientY - rect.top) * this.canvas.height / rect.height)),
};
}

Expand Down Expand Up @@ -528,7 +536,11 @@ class KeyframePreviewEditor

this.bezier_editor = new BezierEditor(this._on_change.bind(this), size, size);
container.appendChild(this.bezier_editor.canvas);
this.bezier_editor.pad = this.bezier_editor.radius + 1;

// Allow supernormal Y
this.bezier_editor.clampY = false;
this.bezier_editor.pad = this.bezier_editor.radius + 50;

this.bezier_editor.scale_x = this.bezier_editor.width - this.bezier_editor.pad * 2;
this.bezier_editor.scale_y = -this.bezier_editor.height + this.bezier_editor.pad * 2;
this.bezier_editor.add_point(0, 0, false).add_out_tan(
Expand Down
8 changes: 2 additions & 6 deletions schema/properties/easing-handle.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,13 @@
"$ref": "#/$defs/values/vector",
"items": {
"type": "number",
"default": 0,
"minimum": 0,
"maximum": 1
"default": 0
},
"minItems": 1
},
{
"type": "number",
"default": 0,
"minimum": 0,
"maximum": 1
"default": 0
}
]
}
Expand Down

0 comments on commit 04f09a5

Please sign in to comment.