From c5e5f13fecb0f62bd847818d210de5a9dc300520 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Fri, 23 Oct 2026 00:24:14 +0100 Subject: [PATCH] Document morph transitions for mobile v4 Adds a "Morphing elements between screens" section to the routing page for `@navigate.morph` and `Transition::Morph`, from NativePHP/mobile-air#372. It covers matching elements by `ref`, the morph, morph-duration and morph-easing attributes, which elements morph, and the native chrome case where nothing does. Also lists the new transition with the existing ones, points to it from Gestures & Animation, and notes on the testing page that a ref now reaches UI drivers and can make an element morph. Co-Authored-By: Claude Opus 5.5 --- .../docs/mobile/4/digging-deeper/gestures.md | 4 +- .../docs/mobile/4/testing/interactions.md | 9 ++ .../views/docs/mobile/4/the-basics/routing.md | 90 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/resources/views/docs/mobile/4/digging-deeper/gestures.md b/resources/views/docs/mobile/4/digging-deeper/gestures.md index 6bf8fc3cc..36708d424 100644 --- a/resources/views/docs/mobile/4/digging-deeper/gestures.md +++ b/resources/views/docs/mobile/4/digging-deeper/gestures.md @@ -127,6 +127,8 @@ the native side plays it locally on touch. Three tools, three jobs: **`animate-*` props** for state-driven motion (a value changed — ease to it), **press feedback** for instant touch response, and **shared values** for finger-tracking or refresh-rate animation. For -motion *between screens*, see [Navigation](../the-basics/routing#custom-transitions). +motion *between screens*, see [Navigation](../the-basics/routing#custom-transitions), and +[Morphing elements between screens](../the-basics/routing#morphing-elements-between-screens) to carry an element from +one screen to the next. diff --git a/resources/views/docs/mobile/4/testing/interactions.md b/resources/views/docs/mobile/4/testing/interactions.md index 179ec49f5..0888573d6 100644 --- a/resources/views/docs/mobile/4/testing/interactions.md +++ b/resources/views/docs/mobile/4/testing/interactions.md @@ -55,6 +55,15 @@ Native::test(HapticsDemo::class) Refs make a test read cleanly and survive copy changes — the label can move without breaking the test. + + +A `ref` also reaches the platform's accessibility tree, as the accessibility identifier on iOS and the resource ID on +Android, so tools that drive the real app, such as Maestro or XCUITest, can find elements by the same name. + +The same `ref` on two screens makes that element [morph between them](../the-basics/routing#morphing-elements-between-screens) +when you navigate with the `morph` transition. Add `morph="none"` to an element whose `ref` is only there for tests +if you don't want that. + ## Pressing - `tap($target)` — press by `ref` or visible text (the friendliest default). diff --git a/resources/views/docs/mobile/4/the-basics/routing.md b/resources/views/docs/mobile/4/the-basics/routing.md index 1310ff3de..d8d0154e0 100644 --- a/resources/views/docs/mobile/4/the-basics/routing.md +++ b/resources/views/docs/mobile/4/the-basics/routing.md @@ -96,6 +96,7 @@ Available cases: - `Transition::FadeFromBottom` - subtle vertical fade - `Transition::ScaleFromCenter` - zoom-in effect - `Transition::ParallaxPush` - incoming screen slides in from the right while the outgoing screen drifts partially left underneath +- `Transition::Morph` - cross-fade while elements that share a `ref` move between the two screens. See [Morphing elements between screens](#morphing-elements-between-screens) - `Transition::None` - swap with no animation ## Navigating from Blade @@ -149,6 +150,7 @@ Available transition modifiers: - `fadeFromBottom` - `scaleFromCenter` - `parallaxPush` +- `morph` - `none` ### Action modifiers @@ -174,6 +176,94 @@ to `replace` or `exitToWeb`: Transition and action modifiers can be combined — `@navigate.replace.fade='/login'`. +## Morphing elements between screens + + + +The morph transition carries an element from where it sits on one screen to where it sits on the next. A thumbnail +grows into a header image, or an avatar slides into place on a profile page. If you've used the web's View +Transitions API, it's the same idea. + +Give the element the same `ref` on both screens, then navigate with the `morph` modifier: + +@verbatim +```blade +{{-- The album list --}} +@foreach ($albums as $album) + + + +@endforeach +``` +@endverbatim + +@verbatim +```blade +{{-- The album screen --}} + + + + + Back + + +``` +@endverbatim + +Tapping an album cross-fades the two screens while the cover moves and grows from the small tile into the header. +Going back with `@navigate.back.morph` plays it in reverse. + +From a component, pass `Transition::Morph`: + +```php +use Native\Mobile\Edge\Transition; + +$this->navigate('/album/3')->transition(Transition::Morph); +``` + +### Shaping the morph + +These attributes change how an element travels. They're read from the element on the screen you're navigating to, +so put them on both elements if you want the same motion in both directions. + +- `morph` - what moves. `frame` (the default) moves and resizes the element. `position` moves it at its final size. + `size` resizes it in place, at its final position. `none` stops the element morphing. +- `morph-duration` - length in milliseconds. The default is `350`, the same as the cross-fade behind it. +- `morph-easing` - the curve: `linear`, `ease-in`, `ease-out`, `ease-in-out` (the default) or `spring`. + +@verbatim +```blade + + ... + +``` +@endverbatim + + + +### Which elements morph + +- An element morphs only when its `ref` is on both screens. Everything else cross-fades with its screen. +- Elements only morph under the `morph` transition. A `ref` that appears on both screens does nothing during a slide + or a fade. +- Keep each `ref` unique within a screen. If two elements share one, you can't be sure which of them moves. +- `ref` is also how [tests target elements](../testing/interactions#by-ref). If you've reused a test `ref` on two + screens and don't want those elements to morph, add `morph="none"` to either of them. + +### Native chrome + +A morph needs one whole screen to replace another. Inside native chrome that doesn't always happen. Pushing from one +screen with a native top bar to another, or switching between tabs of a bottom nav, is animated by the platform's +own navigation, and nothing morphs. + +Morphs do run between screens that have no native chrome, and whenever the chrome changes, such as a tab screen +opening a detail screen that has a top bar instead. See [Layouts](layouts) for how screens get their chrome. + ## Reading params and data ```php