- skills/README.md: provenance + index - SKILL.md: merged design-engineering doctrine, Makepad-native (animator, script_mod!) - EASING.md: CSS cubic-bezier -> Ease.Bezier translation verified against dev@b41e740 - RECIPES.md: press scale 0.96, toast enter/exit, sheet settle w/ velocity handoff, stagger, reduced motion - PORTS-AUDIT.md: Before/After audit of rider/koboyo/insurance with file:line refs
69 lines
3.6 KiB
Markdown
69 lines
3.6 KiB
Markdown
# Easing translation: CSS / Motion → Makepad `Ease`
|
||
|
||
Verified against `widgets/src/animator.rs` on the dev branch (`b41e740`).
|
||
|
||
## The key fact
|
||
|
||
Makepad's `Ease::Bezier {cp0, cp1, cp2, cp3}` **is** CSS `cubic-bezier(x1, y1, x2, y2)`:
|
||
`cp0 = x1`, `cp1 = y1`, `cp2 = x2`, `cp3 = y2` (confirmed in `Ease::map` — the x-solve uses
|
||
`3.0 * cp0`, the y-evaluation uses `3.0 * cp1`). Every named curve Emil ships translates exactly.
|
||
|
||
## Emil's required custom curves
|
||
|
||
| Name | CSS | Makepad DSL |
|
||
|---|---|---|
|
||
| Strong ease-out (UI default) | `cubic-bezier(0.23, 1, 0.32, 1)` | `ease: Ease.Bezier {cp0: 0.23, cp1: 1.0, cp2: 0.32, cp3: 1.0}` |
|
||
| Strong ease-in-out (on-screen movement) | `cubic-bezier(0.77, 0, 0.175, 1)` | `ease: Ease.Bezier {cp0: 0.77, cp1: 0.0, cp2: 0.175, cp3: 1.0}` |
|
||
| iOS drawer/sheet curve (Ionic/Vaul) | `cubic-bezier(0.32, 0.72, 0, 1)` | `ease: Ease.Bezier {cp0: 0.32, cp1: 0.72, cp2: 0.0, cp3: 1.0}` |
|
||
| Jakub's polish curve | `cubic-bezier(0.2, 0, 0, 1)` | `ease: Ease.Bezier {cp0: 0.2, cp1: 0.0, cp2: 0.0, cp3: 1.0}` |
|
||
|
||
## Built-in equivalents (cheaper to type, slightly weaker)
|
||
|
||
| Intent | CSS-ish | Makepad built-in | Strength |
|
||
|---|---|---|---|
|
||
| Enter/exit default | `ease-out` | `OutQuad` → `OutCubic` → `OutQuart` → `OutQuint` → `OutExp` | pick further right for more punch |
|
||
| Movement/morph | `ease-in-out` | `InOutQuad` → `InOutCubic` → `InOutQuart` | |
|
||
| Hover/color | `ease` | `OutQuad` (upstream `check_box.rs` uses this) | |
|
||
| Constant motion | `linear` | `Linear` | |
|
||
| Overshoot (rare, gesture-born only) | spring w/ bounce | `OutBack` (subtle) — avoid `OutElastic`/`OutBounce` in product UI | |
|
||
|
||
**Banned in UI:** `InQuad/InCubic/InQuart/InQuint/InExp/InSine/InCirc/InElastic/InBack/InBounce`
|
||
as the ease of anything entering, exiting, or responding to the user. (`In*` is only legitimate
|
||
for an element *accelerating away* as part of a choreographed hand-off, e.g. upstream
|
||
`slide_panel.rs` uses `InQuad` for the outgoing panel.)
|
||
|
||
## The spring stand-in: `ExpDecay`
|
||
|
||
Makepad has no parametric spring in the animator, but `Ease.ExpDecay {d1, d2}` is an
|
||
exponential-decay settle that reads like a critically-damped spring and is what upstream
|
||
widgets ship for "alive" motion:
|
||
|
||
| Upstream use | Values |
|
||
|---|---|
|
||
| `file_tree.rs` open/close | `Ease.ExpDecay {d1: 0.80, d2: 0.97}` / `{d1: 0.82, d2: 0.95}` |
|
||
| `fold_button.rs` / `fold_header.rs` | `ExpDecay {d1: 0.96, d2: 0.97}` open, `{d1: 0.98, d2: 0.95}` close |
|
||
| Default | `{d1: 0.82, d2: 0.97}` |
|
||
|
||
Rules of thumb: lower `d1` = faster initial move (snappier); `d2` shapes the tail. Stay inside
|
||
d1 0.80–0.98, d2 0.95–0.97 unless you can defend the value. No overshoot ever comes out of
|
||
ExpDecay — for gesture-released momentum with a real bounce use
|
||
`scroll_motion::rubber_band_bounce` driven by `NextFrame` (see RECIPES §4).
|
||
|
||
## Duration reference (`Play::Forward {duration}` is seconds)
|
||
|
||
| Element | Seconds |
|
||
|---|---|
|
||
| Press feedback release | 0.1–0.16 |
|
||
| Hover | 0.0–0.1 |
|
||
| Tooltip/popover | 0.125–0.2 |
|
||
| Dropdown/select/side drawer | 0.15–0.25 |
|
||
| Modal/bottom sheet/toast | 0.2–0.5 |
|
||
| UI ceiling | 0.3 (sheets/modals may reach 0.5) |
|
||
|
||
`Play` variants: `Forward {duration}`, `Snap` (instant — use for press-down),
|
||
`Reverse`, `Loop {duration, end}` (spinners: keep them fast — a faster spinner makes the same
|
||
load *feel* shorter), `ReverseLoop`, `BounceLoop`.
|
||
|
||
Keyframes inside a state: `timeline(t0 v0 t1 v1 …)` / `[{time: 0.0, value: 0.0}, …]` — reserve
|
||
for one-shot choreography (the CSS-keyframes analog); plain state targets retarget smoothly and
|
||
are what interactive toggles should use.
|