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

时间:2014-11-29 03:12:03

标签: javascript jquery css reactjs

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

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

是否有推荐的方法在React.js中执行此操作,可能使用受欢迎的插件?

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

  1. 以自然顺序显示包含DOM节点的列表,并具有相对定位。通过相对定位,其他小的变化 - CSS或JS触发 - 到单个项目'正如预期的那样,将其他人转移。
  2. 在一个步骤中改变所有DOM节点,使其实际相对坐标成为新的绝对坐标 - 一个不会导致视觉变化的DOM更改。
  3. 将其父节点中的DOM节点重新排序为新的排序顺序 - 另一个不会导致视觉变化的DOM更改。
  4. 动画显示所有节点'根据新订单中所有前面项目的高度,对其新计算值进行顶部偏移。这是唯一在视觉上活跃的一步。
  5. 将所有项目DOM节点变回非偏移相对定位。同样,这不会导致视觉上的变化,但是现在相对定位的DOM节点,在底层列表的自然顺序中,将通过适当的移位来处理内部悬停/扩展/等样式更改。
  6. 现在我希望以React-ish的方式产生类似的效果......)

8 个答案:

答案 0 :(得分:33)

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

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

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

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

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

答案 1 :(得分: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作为替代方案。

答案 2 :(得分:9)

React Shuffle非常扎实且最新。它的灵感来自Ryan Florences Magic Move演示

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

答案 3 :(得分:3)

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

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

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

Animating lists with CSS3

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

Disclamer ......我从来没有尝试过这个;)

答案 4 :(得分:1)

我刚刚发现了这个图书馆:https://www.npmjs.com/package/react-animated-list,非常棒,你只需要通过孩子们:

import { AnimatedList } from 'react-animated-list';
import { MyOtherComponent } from './MyOtherComponent';

const MyComponent = ({myData}) => (
  <AnimatedList animation={"grow"}>
    {otherComponents.map((c) => (
      <MyOtherComponent key={c.id} />
    ))}
  </AnimatedList>
)

演示:https://codesandbox.io/s/nifty-platform-dj1iz?fontsize=14&hidenavigation=1&theme=dark

Boom 它对你有用。

答案 5 :(得分:0)

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

最后,使用外部库来操作元素更容易。他们被渲染后的利润率。

答案 6 :(得分:0)

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

当列表项的props.index更改时,旧位置被捕获在getSnapshotBeforeUpdate()和componentDidUpdate()中,将应用css转换:translateY(),以便该项目看起来像是从旧位置到当前位置。 / p>

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

答案 7 :(得分:-3)

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