web-dev-qa-db-ja.com

リストの並べ替えをアニメーション化するreact.jsにやさしい方法は何ですか?

スコア順に並べられたアイテムのリストがあり、react.jsによって、長方形のアイテムの垂直方向のリストとしてレンダリングされています(一番上のスコア)。個々のアイテムをホバーなどでクリックすると、追加情報が表示/非表示になり、垂直方向の高さが変わります。

スコアをわずかに変更する新しい情報が到着し、再ソート後に一部のアイテムのランクが高くなり、その他のアイテムのランクが低くなります。すぐに新しい場所に表示されるのではなく、アイテムを新しい位置に同時にanimateしたいです。

React.jsでこれを行うための推奨される方法はありますか、おそらく人気のあるアドオンを使用しますか?

(D3を使用した同様の過去の状況で、私が使用した手法はおおよそ次のとおりでした。

  1. 相対位置を使用して、自然な順序でアイテムDOMノードを含むリストを表示します。相対配置では、CSSまたはJSトリガーのその他の小さな変更により、個々のアイテムの範囲が予想どおり他のアイテムにシフトします。
  2. すべてのDOMノードを1ステップで変更して、実際の相対座標を新しい絶対座標(視覚的な変化を引き起こさないDOMの変更)にします。
  3. 親内のアイテムDOMノードを新しい並べ替え順序に並べ替えます-視覚的な変更を引き起こさない別のDOM変更。
  4. 新しい順序で先行するすべてのアイテムの高さに基づいて、すべてのノードのトップオフセットを新しい計算値にアニメーション化します。これは視覚的にアクティブな唯一のステップです。
  5. すべてのアイテムDOMノードを非オフセット相対位置に戻します。繰り返しますが、これにより視覚的な変更は発生しませんが、現在の相対的な位置にあるDOMノードは、基になるリストの自然な順序で、適切なシフトで内部ホバー/エキスパンドなどのスタイルの変更を処理します。

今、私はReactっぽい方法で同様の効果を期待しています...)

45
gojomo

私はちょうどこの問題に取り組むモジュールをリリースしました

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

マジックムーブ/シャッフルとは異なるいくつかのことを行います:

  1. ハードウェアアクセラレーション60FPS +アニメーションにFLIPテクニックを使用します
  2. 遅延または持続時間を増分的にオフセットすることにより、シャッフルを「人間化」するオプションを提供します
  3. 中断を適切に処理し、奇妙なグリッチ効果はありません
  4. 開始/終了コールバックのような他のきちんとしたものの束

デモをご覧ください:

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

39
Joshua Comeau

React Shuffleは堅牢で最新のものです。 Ryan Florences Magic Moveデモに触発された

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

9
Tim Arney

これは少し古い質問であり、おそらく今までに解決策を見つけたと思いますが、この質問に出くわした人は、ライアンフローレンスのMagicMoveライブラリをチェックしてください。 Reactあなたが記述する正確なシナリオを処理するためのライブラリ: https://github.com/ryanflorence/react-magic-move

実際にご覧ください: https://www.youtube.com/watch?v=z5e7kWSHWTg#t=424

編集:これはReact 0.14で壊れています。以下のTim Arneyによって提案された代替案として React Shuffle を参照してください。

9
Andru

私が頭の中でこれを達成するために私が考えることができる最良の方法は、最初に、ここで説明するようにあなたの要素のすべてにキーを与えることを確認することです。

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

次に、次のようなCSS3アニメーションを定義します。

CSS3を使用したリストのアニメーション化

基本的にレンダー関数では、要素の配置場所を計算します。ReactJSは、要素を配置する場所に配置します(各要素に毎回同じキーを指定してください!これは、ReactJSがDOM要素を再利用するために重要です正しく)。少なくとも理論的には、reactjs/javascript以外のアニメーションの残りの部分はCSSが処理する必要があります。

Disclamer ...私は実際にこれを試したことがない;)

3
Chris

アニメーションにサードパートライブラリを使用したくなかったため、「 [〜#〜] flip [〜#〜] 」を実装しましたReactライフサイクルメソッドgetSnapshotBeforeUpdate()およびcomponentDidUpdate()を使用したアニメーション手法。

リストアイテムのprops.indexが変更されると、古い位置がgetSnapshotBeforeUpdate()およびcomponentDidUpdate()でキャプチャされ、css transform:translateY()が適用され、アイテムが古い位置から現在の位置にアニメーション表示されます。

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;
}
1
Boog

要素の位置はDOMエンジンに大きく依存しているため、Reactコンポーネント内で純粋な方法でトゥイーンとアニメーションを実装することは非常に難しいと思います。少なくとも、要素の現在位置を保持するストア、要素の次の位置を保持する(または前のストアと結合する)ストア、および要素ごとにオフセットマージンを適用するrender()メソッドが必要です。最初の場所で次の位置を計算する方法も課題ですが、要素が位置のみを交換する場合、現在の位置でストアを使用して要素#0を交換できます要素#1、次の位置でオフセットを交換します。

最終的には、外部ライブラリを使用して、レンダリング後に要素のマージンを操作する方が簡単です。

0
Rygu