solid-router icon indicating copy to clipboard operation
solid-router copied to clipboard

Feature routing with transition duration

Open KevinBLT opened this issue 2 years ago • 5 comments

We are migrating from AngularDart to SolidJS right now.

When evaluating the existing router solution there is a key peace we found missing for our applications to behave the same way. In AngularDart, we had our own routing component, which more or less did the same as this one.

But the feature missing is the ability to make smooth route transitions.

These are enabled by giving the previous active and the now active routes a user given amount of time in a "route-in" and "route-out" state. The user can use the state to make CSS animations.

The changes should be non-breaking for existing applications, although I would like to rename "isRouting" to "isLoading", since it's the loading state, although while loading it's also routing, so this would also be non-breaking.

Todos:

  • [x] Defer state "Show when={}" state by user given time
  • [ ] Get the time from the user (should be a value or a function, in ms)
  • [x] Let components get their routing state (isRouteOut, isRouteIn, isRoute)
  • [ ] Detect if the routing is a "back" routing, so an animation could run backwards
  • [ ] Correctly get loading state and inform the user about the routing states
  • [ ] Write tests

Behavior in the DOM:
ex2

Behavior on the page (slide-in/out but no handling of back in the way that the old elements comes back instead of acting as a new one): ex

I might not have used solid correctly at every place, I would love some help after it's working to clean up some parts if necessary.

KevinBLT avatar Aug 08 '22 15:08 KevinBLT

Only thing I'm unclear about is how this plays with Solid's Suspense Transition feature on timing. Animations are definitely a weak point for me, but I am unclear at which point the animation stuff happens. Like if the pattern is to hold on the current page until the new data is available, would animation then run at that point when the new page is finished.

Like the timing is:

  1. Initialize navigation
  2. Render the next route off the screen in Suspense Transition
  3. Async completes (or no Async) use some time to animate out
  4. Animate in the page that was rendered off screen

I haven't tried this yet but I wonder how different this is from wrapping the router in a Transition Group. Like from Solid-Transition-Group or Solid-Flip

The router definitely wants to render the next page without intrusion so the orchestration probably needs to happen above that decision point.

ryansolid avatar Aug 08 '22 19:08 ryansolid

I think I simply did not observe Transition Group. I will try if I can make a replacement with that, so I can regenerate the routing animations as before. One example was a box that explodes and the next page comes out of that.

It might not be needed here in the router (as I thought). Nonetheless I would like some more optional signals/events to better determine the state of the router. I will come back at this tomorrow after I tested the transition group for that.

KevinBLT avatar Aug 08 '22 19:08 KevinBLT

To be fair Transition Group is not my most proud code, it's kinda buggy I'm told.. Solid-Flip is better but I am not sure it supports single swap transitions.

It is possible it makes sense for the Router to handle this as well. I just am thinking about mechanically how this should work. Because getting it wrapped around Suspense Transitions is tricky. It might be for that case alone that we will need special handling.

ryansolid avatar Aug 08 '22 22:08 ryansolid

Hi Ryan,

I tried it with "TransitionGroup", which threw an error "in the getRect()" function and with "Transition" around every "Route" nothing happens.

export default function App() {
  const enter = (el, done) => {
    const a = el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 600 });
    
    a.finished.then(done);

    console.log('enter: Call?');
  }, exit = (el, done) => {
    const a = el.animate([{ opacity: 1 }, { opacity: 0 }], {
      duration: 600
    });

    a.finished.then(done);

    console.log('exit: Call?');
  };

  return (
    <Routes>

      <Transition onEnter={enter} onExit={exit} >
        <Route path="/"  component={Root2} />
      </Transition>

      <Transition onEnter={enter} onExit={exit} >
        <Route path="/c" component={C}  />
      </Transition>

    </Routes>
  );
}

render(() => <Router><App /></Router>, document.querySelector('body'));

Am I doing something wrong? console.log was never called.

For the other topics:

I think a mix of router and transition(group) might be a possible solution. Maybe using something like "AnimatedRoutes".

In general to work as intended (at least for our use cases) it should:

  • First load the next page (missing scripts, css, ...) (state "loading") and then do the animation resulting in load time + animation time.

So for the navigate function I would have needed something like this: navigate(uri, onLoadedCallback) : Promise, so I needed to now when it's loaded and when it has transitioned. I used this to create all kind of fancy page transitions.

Also in the case of routing it's nice to now if we are going "backwards", so the animation can run backwards otherwise it feels awkward to click the "back" button of the browser (see the slide-in/out example).

KevinBLT avatar Aug 09 '22 10:08 KevinBLT

Hmm it needs to wrap the outlet not the individual routes.. The <Route> is just a definition.. it is the <Routes> component or <Outlet> where the actual swap happens. Of course that doesn't give a mechanism per page to handle this which again suggests it may need to be part of the router. But mechanically that is the "above" I'm talking about,

ryansolid avatar Aug 09 '22 15:08 ryansolid

Hey @ryansolid, today I checked again and it seems to work as I would need it to be. Thanks for looking at this and helping me out! Perfect! :-)

Will work like that (+ the onEnter/onExit handlers):
image

Did correctly set out and in state on the elements with page changes.

KevinBLT avatar Sep 14 '22 10:09 KevinBLT

That's awesome that just works.. I think we probably should advertise this more. Thanks for reporting back.

ryansolid avatar Sep 14 '22 13:09 ryansolid