Website-Suche

javascript - Animationen funktionieren beim Eintreten nicht (Tailwind, TransitionGroup, Typescript)


Ich habe eine Wrapper-Komponente erstellt, um eine Liste von Klassennamen über einen String bereitzustellen, um Tailwind-Utility-Übergänge zu unterstützen. Dies soll in einer TransitionGroup-Komponente verschachtelt werden, um Animationen mehrerer Elemente, wie einer Liste, zu unterstützen.

Ich kann problemlos animieren, aber das Einsteigen funktioniert nicht. Können Sie mir bitte helfen, den Fehler in diesem Code zu finden?

react 16.13.1 react-transition-group 4.4.1

Sandbox: https://codesandbox.io/s/delicate-feather-ymozq

(Dort einloggen, um die Div-Referenz zu überprüfen, falls die Klassen nicht angewendet würden, aber sie scheinen einwandfrei zu funktionieren)

import React, { ReactNode } from "react";
import { Transition as ReactTransition } from "react-transition-group";

interface TransitionProps {
  in?: boolean;
  timeout: number;
  enter?: string;
  enterFrom?: string;
  enterTo?: string;
  leave?: string;
  leaveFrom?: string;
  leaveTo?: string;
  children: ReactNode;
}

export function CSSTransition(props: TransitionProps) {
  const { enter, enterFrom, enterTo, leave, leaveFrom, leaveTo } = props;
  const nodeRef = React.useRef<HTMLDivElement>(null);

  const enterClasses = splitClasses(enter);
  const enterFromClasses = splitClasses(enterFrom);
  const enterToClasses = splitClasses(enterTo);
  const leaveClasses = splitClasses(leave);
  const leaveFromClasses = splitClasses(leaveFrom);
  const leaveToClasses = splitClasses(leaveTo);

  function splitClasses(string: string | undefined): string[] {
    if (string) return string.split(" ").filter((s) => s.length);
    return [];
  }

  function addClasses(classes: string[]) {
    nodeRef.current?.classList.add(...classes);
  }

  function removeClasses(classes: string[]) {
    nodeRef.current?.classList.remove(...classes);
  }

  return (
    <ReactTransition
      in={props.in}
      nodeRef={nodeRef}
      timeout={props.timeout}
      unmountOnExit
      onEnter={() => {
        console.log("onEnter", nodeRef);
        addClasses([...enterClasses, ...enterFromClasses]);
      }}
      onEntering={() => {
        console.log("onEntering", nodeRef);
        removeClasses(enterFromClasses);
        addClasses(enterToClasses);
      }}
      onEntered={() => {
        console.log("onEntered", nodeRef);
        removeClasses([...enterToClasses, ...enterClasses]);
      }}
      onExit={() => {
        console.log("onExit", nodeRef);
        addClasses([...leaveClasses, ...leaveFromClasses]);
      }}
      onExiting={() => {
        console.log("onExiting", nodeRef);
        removeClasses(leaveFromClasses);
        addClasses(leaveToClasses);
      }}
      onExited={() => {
        console.log("onExited", nodeRef);
        removeClasses([...leaveToClasses, ...leaveClasses]);
      }}
    >
      <div ref={nodeRef}>{props.children}</div>
    </ReactTransition>
  );
}

Antwort #1

Lösung :

In CSSTransition.tsx, ändern

import { Transition as ReactTransition } from "react-transition-group";

zu

import { CSSTransition as ReactTransition } from "react-transition-group";

Erklärung :

Tailwind-Klassennamen basieren auf CSS-Animationen.

Transition ist nicht dafür ausgelegt, mit CSS-Animationen zu arbeiten, da die Methoden onEnter und onEntering so schnell aufgerufen werden, dass zwischen ihnen kein DOM-Neuzeichnen (ein Frame der Animation) erfolgt. Daher werden alle Klassen, die Sie für enterTo festlegen, sofort angewendet, ohne dass eine Chance besteht, zu ihnen zu übergehen.

CSS-Transition fügt hingegen eine -enter-Klasse hinzu, während es das Element erstellt, und fügt dann eine -enter-active-Klasse im nächsten Takt/Frame hinzu. Schließlich werden nach dem Timeout alle Klassen entfernt, mit Ausnahme der Hinzufügung einer -done-Klasse.

Der Übergang zwischen der -enter und der -enter-active Klasse ist der Schlüssel, um CSS-Übergänge zu ermöglichen. Ein Tick/Frame wird zwischen diesen Klassen hinzugefügt, was der Bibliothek erlaubt, eine Neuzeichnung zu erzwingen. Diese Neuzeichnung zwingt die onEntering-Klassen dazu, wie beabsichtigt zu den onEntered-Klassen zu wechseln.

Mit anderen Worten, Transition wechselt sofort in den enterTo-Zustand, während CSSTransition einen Frame zwischen dem Klassentausch lässt, der benötigt wird, damit die Animation stattfinden kann.

Das DOM animiert/ändert sich normalerweise mit maximal 60 Bildern pro Sekunde (abhängig von der Implementierung/der Bildwiederholrate des Endbenutzers), wodurch 16 ms für jedes Bild verbleiben; es kümmert sich nicht darum, was zwischen diesen 16 ms passiert. Wenn der Endzustand nach Klassenänderungen Ihr enterTo-Zustand anstelle Ihres enterFrom-Zustands ist, ignoriert es den enterFrom-Zustand und zeigt Ihnen daher niemals die Tailwind-Animationen.

Quellen :

  • React Transition Groups-Dokumentation
  • React Transition Group Neuzeichen-Code
  • Beispiel dafür, was eine Tailwind-Klasse hinzufügt