javascript - hur kan jag skapa en sidövergång med preact


Jag försöker få en sidövergång med preact-router, jag har redan försökt med preact-transition-group-paketet tillsammans med preact-css-transition-group-paketet, det ger mig ett fel, men här är ändå min grundläggande uppsättning:

import { h, FunctionComponent } from 'preact';
import Router from 'preact-router';

const App: FunctionComponent = () => {
  return (
    <Router>
      <div path="/1" style="padding: 50px">
        page 1 <a href="/2">page 2</a>
      </div>
      <div path="/2" style="padding: 50px">
        page 2 <a href="/1">page 1</a>
      </div>
    </Router>
  );
};

En TypeScript- och/eller preact-router-lösning är att föredra men inte nödvändig.

Svar #1

Observera: att detta tillvägagångssätt förmodligen inte fungerar med nästlade rutter.

Med detta tillvägagångssätt behöver du omsluta varje rutt i en div med en klass som kommer att animera rutten och sätta key- och path-attributet till rutens path på följande sätt:

function wrapRoute(route: h.JSX.Element): h.JSX.Element {
  return ( // the in class is the animation and the page class is makes sure that the old and new routes overlap
    <div path={route.props.path} class="in page" key={route.props.path}>
      {route}
    </div>
  );
}

då behöver du lägga till två reaktiva variabler så här:

const [previousEl, setPreviousEl] = useState<ComponentChildren | null>(null);
const [outEl, setOutEl] = useState<JSX.Element | null>(null);

efter det kan du lyssna på onChange-händelsen från preact-router och sätta outEl till en div som omsluter previousEl och har en klass som kommer animera rutten ut, sedan lägga till en onAnimationEnd-lyssnare så att du kan sätta outEl till null när ut-animeringen har avslutats, sista sak du behöver göra är att sätta previousEl till e.current.props.children, din routerkomponents lyssnare bör se ut så här:

<Router onChange={(e) => {
  if (previousEl) {
    setOutEl(
      <div class="out page" key={e.previous} onAnimationEnd={() => setOutEl(null)}>
        {previousEl}
      </div>
    );
  }
  if (e.current) {
    setPreviousEl(e.current.props.children);
  }
}}
>
{routes} // this is an array containing all the wrapped routes.
</Router>

Det sista du behöver göra är att skapa animationerna, titta på app.sass-exemplet nedan.

här är det fullständiga exemplet:

app.tsx

import { h, FunctionComponent, JSX, ComponentChildren, Fragment } from 'preact';
import Router from 'preact-router';
import { useState } from 'preact/hooks';
import './app.sass'

const routes = [
  <div style="padding: 50px; background: red" path="/1">
    page 1 <a href="/2">page 2</a> <a href="/3">page 3</a>
  </div>,
  <div style="padding: 50px; background: blue" path="/2">
    page 2 <a href="/1">page 1</a> <a href="/3">page 3</a>
  </div>,
  <div style="padding: 50px; background: green" path="/3">
    page 2 <a href="/1">page 1</a> <a href="/2">page 2</a>
  </div>,
].map((route) => wrapRoute(route));

function wrapRoute(route: h.JSX.Element): h.JSX.Element {
  return (
    <div path={route.props.path} class="in page" key={route.props.path}>
      {route}
    </div>
  );
}

const App: FunctionComponent = () => {
  const [previousEl, setPreviousEl] = useState<ComponentChildren | null>(null);
  const [outEl, setOutEl] = useState<JSX.Element | null>(null);

  return (
    <Fragment>
      <Router
        onChange={(e) => {
          if (previousEl) {
            setOutEl(
              <div class="out page" key={e.previous} onAnimationEnd={() => setOutEl(null)}>
                {previousEl}
              </div>
            );
          }
          if (e.current) {
            setPreviousEl(e.current.props.children);
          }
        }}
      >
        {routes}
      </Router>
      {outEl}
    </Fragment>
  );
};

export default App;

app.sass

.page
  position: absolute
  top: 0
  bottom: 0
  left: 0
  right: 0

.out
  animation: out 200ms forwards

@keyframes out
  0%
    opacity: 1
    transform: translateX(0)
  100%
    opacity: 0
    transform: translateX(100vw)

.in
  animation: in 200ms forwards

@keyframes in
  0%
    opacity: 0
    transform: translateX(-100vw)
  100%
    opacity: 1
    transform: translateX(0)

Svar #2

Det bästa sättet att skapa en animation är att introducera en annan HOC - Higher Order Component som omsluter din sida och använda det som ett routingsmål. Det skulle vara något som:

import { h, FunctionComponent } from 'preact';
import Router from 'preact-router';

const App: FunctionComponent = () => {
  return (
    <Router>
      <PageAnimation path="/1">
          <div style="padding: 50px">
            page 1 <a href="/2">page 2</a>
          </div>
      </PageAnimation>
      <PageAnimation path="/2">
        <div style="padding: 50px">
          page 2 <a href="/1">page 1</a>
        </div>
      </PageAnimation>
    </Router>
  );
};

Din implementeringskod skulle vara något i stil med - Hur man gör en övergångsanimation.