wake-up-neo.net

Was ist eine reag.js-freundliche Methode, um eine Listenumordnung zu animieren?

Ich habe eine Liste von Elementen mit Bewertungen, sortiert nach Bewertungen, dargestellt von durch react.js als vertikal ausgerichtete Liste rechteckiger Elemente (höchste Bewertung oben). Hover und andere Klicks auf die einzelnen Elemente können zusätzliche Informationen anzeigen/ausblenden und ihre vertikale Höhe ändern.

Es werden neue Informationen angezeigt, die die Ergebnisse leicht ändern, sodass einige Elemente nach einer Umsortierung höher und andere niedriger eingestuft werden. Ich möchte, dass die Elemente gleichzeitig animieren an ihren neuen Positionen _, anstatt sofort an ihren neuen Positionen zu erscheinen. 

Gibt es einen empfohlenen Weg, dies in React.js zu tun, vielleicht mit einem beliebten Add-On?

(In einer ähnlichen Situation mit D3 war eine Technik, die ich verwendet habe, ungefähr:

  1. Zeigen Sie die Liste mit den Element-DOM-Knoten in ihrer natürlichen Reihenfolge mit relativer Positionierung an. Bei der relativen Positionierung verschieben andere kleine Änderungen - durch CSS oder JS ausgelöst - in das Ausmaß der einzelnen Elemente, andere wie erwartet.
  2. Mutieren Sie alle DOM-Knoten in einem einzigen Schritt, um ihre tatsächlichen Relativkoordinaten als neue absolute Koordinaten zu definieren - eine DOM-Änderung, die keine visuelle Änderung bewirkt.
  3. Ordnen Sie die Element-DOM-Knoten innerhalb ihres übergeordneten Elements in die neue Sortierreihenfolge um - eine weitere DOM-Änderung, die keine visuelle Änderung bewirkt.
  4. Animieren Sie die Top-Offsets aller Knoten basierend auf den Höhen aller vorhergehenden Elemente in der neuen Reihenfolge mit ihren neu berechneten Werten. Dies ist der einzige visuell aktive Schritt.
  5. Mutieren Sie alle Element-DOM-Knoten zurück zur relativen Positionierung ohne Versatz. Dies bewirkt wiederum keine visuelle Änderung, aber die nun relativ positionierten DOM-Knoten werden in der natürlichen Reihenfolge der zugrunde liegenden Liste interne Änderungen im Hover/Expand/etc-Stil mit korrekter Verschiebung verarbeiten.

Jetzt hoffe ich auf einen ähnlichen Effekt auf eine reaktisierende Weise ...)

39
gojomo

Ich habe gerade ein Modul veröffentlicht, um genau dieses Problem anzugehen

https://github.com/joshwcomeau/react-flip-move

Es macht ein paar Dinge anders als Magic Move/Shuffle:

  1. Es verwendet die FLIP-Technik für hardwarebeschleunigte 60FPS + -Animationen
  2. Es bietet Optionen zum "Humanisieren" des Shuffles, indem Verzögerung oder Dauer schrittweise ausgeglichen werden
  3. Es behandelt Unterbrechungen elegant, keine seltsamen Störimpulse 
  4. Haufen andere nette Dinge wie Start/Ziel-Callbacks

Schauen Sie sich die Demos an:

http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle

27
Joshua Comeau

React Shuffle ist solide und aktuell. Es ist von Ryan Florences Magic Move Demo inspiriert 

https://github.com/FormidableLabs/react-shuffle

9
Tim Arney

Ich weiß, dass dies eine alte Frage ist, und Sie haben wahrscheinlich bereits eine Lösung gefunden, aber für alle anderen, die auf diese Frage stoßen, sollten Sie sich die MagicMove-Bibliothek von Ryan Florence ansehen. Es handelt sich um eine React-Bibliothek, die genau das von Ihnen beschriebene Szenario behandelt: https://github.com/ryanflorence/react-magic-move

In Aktion sehen: https://www.youtube.com/watch?v=z5e7kWSHWTg#t=424

Bearbeiten: Dies ist in React 0.14 nicht mehr möglich. Siehe React Shuffle als Alternative, wie von Tim Arney unten vorgeschlagen.

9
Andru

Ich kann mir am besten vorstellen, dass dies von meinem Kopf aus durchgeführt wird, indem Sie zunächst sicherstellen, dass Sie jedem Ihrer Elemente einen Schlüssel geben, wie hier beschrieben:

http://facebook.github.io/react/docs/multiple-components.html#dynamic-children

Als Nächstes würde ich eine CSS3-Animation ähnlich der folgenden definieren:

Animieren von Listen mit CSS3

Grundsätzlich würden Sie bei Ihrer Renderfunktion berechnen, wohin das Element gehen soll. ReactJS wird das Element dort platzieren, wo es sich befindet (stellen Sie sicher, dass Sie jedem Element jedes Mal den gleichen Schlüssel geben!) richtig). Zumindest theoretisch sollte das CSS für den Rest der Animation außerhalb von reactjs/javascript für Sie sorgen.

Disclamer ... Ich habe das noch nie probiert;)

3
Chris

Da die Position eines Elements stark von der DOM-Engine abhängt, finde ich es wirklich schwierig, das Tweening und die Animation auf reale Weise in den React-Komponenten zu implementieren. Wenn Sie es sauber machen möchten, müssen Sie mindestens Folgendes tun: einen Store zum Speichern der aktuellen Position eines Elements, einen Store zum Speichern der nächsten Position eines Elements (oder zum Kombinieren mit dem vorherigen Store) und ein Rendern ( ) Diese Methode wendet die Versatzränder pro Element an, bis die nächsten Positionen im letzten Frame aktuell sind. Wie Sie die nächsten Positionen überhaupt berechnen, ist auch eine Herausforderung. Da die Elemente jedoch nur Positionen tauschen, können Sie den Store mit den aktuellen Positionen verwenden, um Element # 0 mit Element # 1 zu tauschen, indem Sie deren Offsets in den nächsten Positionen Store tauschen.

Am Ende ist es einfacher, eine externe Bibliothek zu verwenden, um die Ränder der Elemente nach dem Rendern zu bearbeiten.

0
Rygu

Ich wollte keine dritte Bibliothek für meine Animationen verwenden, daher implementierte ich die Animationstechnik " FLIP " mit den React-Lebenszyklusmethoden getSnapshotBeforeUpdate () und componentDidUpdate ().

Wenn sich der props.index des Listenelements ändert, wird die alte Position in getSnapshotBeforeUpdate () und in componentDidUpdate () erfasst. Css transform: translateY () wird angewendet, sodass das Element von der alten Position zur aktuellen Position animiert zu sein scheint.

class TreeListItem extends Component {

  constructor(props) {
    super(props);
    this.liRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps) {
    if (prevProps.index !== this.props.index) {
      // list index is changing, prepare to animate
      if (this.liRef && this.liRef.current) {
        return this.liRef.current.getBoundingClientRect().top;
      }
    }
    return null;
  }


  componentDidUpdate(prevProps, prevState, snapshot) {
    if (prevProps.index !== this.props.index && snapshot) {
      if (this.liRef && this.liRef.current) {
        let el = this.liRef.current;
        let newOffset = el.getBoundingClientRect().top;
        let invert = parseInt(snapshot - newOffset);
        el.classList.remove('animate-on-transforms');
        el.style.transform = `translateY(${invert}px)`;
        // Wait for the next frame so we
        // know all the style changes have
        // taken hold.
        requestAnimationFrame(function () {
          // Switch on animations.
          el.classList.add('animate-on-transforms');
          // GO GO GOOOOOO!
          el.style.transform = '';
        });
      }
    }
  }

  render() {
    return <li ref={this.liRef}>
    </li >;
  }
}

class TreeList extends Component {

  render() {
    let treeItems = [];
    if (this.props.dataSet instanceof Array) {
      this.props.dataSet.forEach((data, i) => {
        treeItems.Push(<TreeListItem index={i} key={data.value} />)
      });
    }

    return <ul>
      {treeItems}
      </ul>;
  }
}
.animate-on-transforms {
  /* animate list item reorder */
  transition: transform 300ms ease;
}

0
Boog