makepad-ports/skills/makepad-ui-craft/EASING.md
andodeki e09f6bbb51 Add makepad-ui-craft skill pack: Emil Kowalski + Jakub Krehel doctrine translated to Makepad 2.0
- 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
2026-09-01 18:36:57 +00:00

69 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.800.98, d2 0.950.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.10.16 |
| Hover | 0.00.1 |
| Tooltip/popover | 0.1250.2 |
| Dropdown/select/side drawer | 0.150.25 |
| Modal/bottom sheet/toast | 0.20.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.