什么是react.js友好的动态列表重新排序的方式?

问题描述 投票:41回答:7

我有一个带分数的项目列表,按分数排序,由react.js呈现为垂直方向的矩形项目列表(顶部最高分)。对各个项目进行悬停和其他点击可能会显示/隐藏额外信息,从而更改其垂直高度。

新信息到达时会略微改变分数,使得某些项目在重新排序后排名更高,而其他项目则更低。我希望这些项目可以同时动画到新的位置,而不是立即出现在新的位置。

是否有推荐的方法在React.js中执行此操作,也许使用流行的附加组件?

(在使用D3的类似过去​​的情况下,我使用的技术大致是:

  1. 以自然顺序显示具有项目DOM节点的列表,具有相对定位。通过相对定位,其他小的变化 - CSS或JS触发 - 对个别项目的程度会按预期转移其他项目。
  2. 在一个步骤中改变所有DOM节点,使其实际相对坐标成为新的绝对坐标 - 不会导致视觉变化的DOM更改。
  3. 将其父项中的DOM节点重新排序为新的排序顺序 - 另一个不会导致视觉变化的DOM更改。
  4. 根据新排序中所有前面项目的高度,将所有节点的顶部偏移设置为其新计算值。这是唯一视觉活跃的步骤。
  5. 将所有项DOM节点变回非偏移相对定位。同样,这不会导致视觉上的变化,但是现在相对定位的DOM节点,在底层列表的自然顺序中,将通过适当的移位来处理内部悬停/扩展/等样式更改。

现在我希望以React-ish的方式产生类似的效果...)

javascript jquery css reactjs
7个回答
39
投票

我刚刚发布了一个模块来解决这个问题

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

它有一些不同于Magic Move / Shuffle的东西:

  1. 它使用FLIP技术进行硬件加速的60FPS +动画
  2. 它通过逐步抵消延迟或持续时间来提供“人性化”混乱的选项
  3. 它优雅地处理中断,没有奇怪的故障影响
  4. 一堆其他整洁的东西,如开始/结束回调

查看演示:

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


9
投票

React Shuffle是坚实的,最新的。它的灵感来自Ryan Florences Magic Move演示

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


9
投票

我意识到这是一个古老的问题,你现在可能已经找到了解决方案,但对于遇到这个问题的其他人来说,请查看Ryan Florence的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作为替代方案。


3
投票

我能想到的最好的方法是首先确保你给每个元素一个键,如下所述:

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

接下来我将定义类似于此的CSS3动画:

Animating lists with CSS3

基本上在你的渲染函数中你会计算元素的位置,ReactJS会将元素放在它想象的位置(确保你每次都给每个元素赋予相同的键!这对于确保ReactJS重用你的DOM元素很重要正确)。至少在理论上,CSS应该在reactjs / javascript之外为你处理动画的其余部分。

免责声明......我从未真正尝试过这个;)


1
投票

我不想为我的动画使用第三方库,所以我使用React生命周期方法getSnapshotBeforeUpdate()和componentDidUpdate()实现了“FLIP”动画技术。

当列表项的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;
}

0
投票

因为元素的位置在很大程度上取决于DOM引擎,我发现在React组件中以纯粹的方式实现补间和动画真的很难。如果你想干净利落,我认为你至少需要:一个商店来保存一个元素的当前位置,一个商店来保存一个元素的下一个位置(或者将它与前一个商店结合起来)和一个渲染( )方法应用每个元素的偏移边距直到最后一帧,下一个位置变为当前。如何计算下一个位置也是一个挑战。但是假设元素只交换位置,你可以使用Store与当前位置交换元素#0和元素#1,通过在下一个位置Store中交换它们的偏移量。

最后,使用外部库在渲染后操纵元素的边距会更容易。


-3
投票

只需使用ReactCSSTransitionGroup。它内置于React with Addons包中,非常适合这个用例!只需确保应用转换的组件已安装到DOM。使用链接示例中的CSS进行漂亮的渐变过渡。

© www.soinside.com 2019 - 2024. All rights reserved.