什么是对列表重新排序进行动画处理的 react.js 友好方式?

Posted

技术标签:

【中文标题】什么是对列表重新排序进行动画处理的 react.js 友好方式?【英文标题】:What's a react.js-friendly way to animate a list-reordering? 【发布时间】:2015-01-27 16:37:31 【问题描述】:

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

新信息的到来会稍微改变分数,在重新排序后,一些项目的排名更高,而另一些项目的排名更低。我希望这些项目同时动画到它们的新位置,而不是立即出现在它们的新位置。

在 React.js 中是否有推荐的方法来做到这一点,也许使用流行的插件?

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

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

现在我希望以类似 React 的方式获得类似的效果...)

【问题讨论】:

你可能想看看:github.com/chenglou/react-tween-state(我至今没用过,但已经计划了很多次了:) 确实看到 react-tween-state 提醒了我一段时间以来我一直在想这个问题,并提出了我的问题。看起来它可能是可用的,像我之前的 D3 方法一样有一些多步骤的黑客攻击......但仍然希望 React 社区中已经存在强烈推荐/优化的方法。 我刚刚找到了这个。看起来和你描述的很接近……而且感觉非常好。 MixItUp MixItUp 很漂亮!而且,非常像我正在寻找的视觉效果。 (我之前使用过,甚至购买过 Metafizzy 的类似 Isotope 的许可证,然后才决定采用上面大致描述的 D3 方法。)但是,现在我专门寻找基于 React.js(并且免费/开源)方法。所以在某种程度上,问题是:如何以对 React 友好的方式实现 MixItUp/Isotope-like 效果? 【参考方案1】:

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

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

它做一些不同于 Magic Move / Shuffle 的事情:

    它将 FLIP 技术用于硬件加速的 60FPS+ 动画 它提供了通过增量抵消延迟或持续时间来“人性化”随机播放的选项 优雅地处理中断,没有奇怪的故障影响 一堆其他简洁的东西,比如开始/结束回调

查看演示:

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

【讨论】:

这看起来很棒。一定要试一试,谢谢。 几年后,这个模块仍然很强大 :) 从那以后,我们引入了进入/离开动画,添加了更多可定制的道具,提高了性能,并缩小了包大小(在现在 5kb gzip!) 嘿,Joshua,我真的很喜欢你的想法。我现在正在使用该组件。这里有一个问题,有没有办法禁用翻转?我需要在某些情况下禁用它 NVM,自己找到了答案,(disableAllAnimations 道具)【参考方案2】:

React Shuffle 是可靠且最新的。它的灵感来自 Ryan Florence 的 Magic Move 演示

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

【讨论】:

FormidableLabs/react-shuffle 不再维护。【参考方案3】:

我意识到这是一个老问题,您现在可能已经找到了解决方案,但是对于遇到此问题的其他人,请查看 Ryan Florence 的 MagicMove 库。这是一个 React 库,可以处理您描述的确切场景:https://github.com/ryanflorence/react-magic-move

查看实际操作:https://www.youtube.com/watch?v=z5e7kWSHWTg#t=424

编辑:这在 React 0.14 中被破坏了。请参阅React Shuffle 作为替代方案,如下 Tim Arney 所建议。

【讨论】:

哇,这看起来完全正确!我已经修改了其他可能的方法,但还没有部署,所以这是一个很大的帮助。 这令人印象深刻!不过,React 0.13 似乎确实存在一些问题(参见 github),我想这至少在 React 0.14 之前不会改变 不幸的是,没有 0.14:github.com/ryanflorence/react-magic-move/issues/15【参考方案4】:

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

当列表项的 props.index 更改时,旧位置会在 getSnapshotBeforeUpdate() 和 componentDidUpdate() 中捕获,应用 css 转换: 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($invertpx)`;
        // 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;

【讨论】:

我在这个列表中最喜欢的答案,因为它不需要第三方库来实现 OP 的要求。 这应该被标记为最佳答案,它简单、干净并且不依赖任何第三方库。【参考方案5】:

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

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

接下来我会定义类似这样的 CSS3 动画:

Animating lists with CSS3

基本上在你的渲染函数中你会计算元素应该去哪里,ReactJS 会把元素放在它应该在的地方(确保你每次给每个元素相同的键!这对于确保 ReactJS 重用很重要你的DOM元素正确)。至少在理论上,CSS 应该为你处理 reactjs/javascript 之外的其余动画。

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

【讨论】:

不起作用。看起来元素在某些时候从 dom 中删除,破坏了 css 转换。 jsfiddle.net/se6mqm3p/1 有趣。看起来反应并不总是尊重键。不过,您可以这样做:jsfiddle.net/se6mqm3p/2 无论如何,我基本上都保持元素的顺序相同。不完全是我最初的想法,但它可以工作。【参考方案6】:

我刚找到这个库: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

繁荣它会为你工作。

【讨论】:

【参考方案7】:

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

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

【讨论】:

【参考方案8】:

只需使用ReactCSSTransitionGroup。 它内置在 React with Addons 包中,非常适合这个用例!只需确保应用过渡的组件已安装到 DOM。使用链接示例中的 CSS 实现漂亮的淡入淡出过渡。

【讨论】:

谢谢,但从我对该链接的阅读来看,它只处理项目的进入/退出——而不是重新排序到新位置。如果没有问题或其他答案中提到的那种外部 React 位置计算,我也看不到扩展它的方法。 (一系列淡入/淡出,即使是同时的,也不是我想要的效果。) 目前这实际上不是一个非常实用的解决方案。除了限制之外,由于它依赖于在许多情况下并不总是触发的浏览器事件,因此它非常有问题(请参阅github.com/facebook/react/issues/1326)。更糟糕的是,似乎甚至没有修复它的时间表(至少根据那个问题线程)。看起来我们必须等待 React 引入更强大的原生转换支持方式。

以上是关于什么是对列表重新排序进行动画处理的 react.js 友好方式?的主要内容,如果未能解决你的问题,请参考以下文章

通过重新排序项目的WPF ListView动画?

使用 angularjs 列出重新排序动画

UWP 动画列表项移动

什么更有效:排序流或排序列表?

应该如何对列表中的项目进行重新排序,并更新多个对象,如何使用 GraphQL 突变进行持久化?

一排书架上摆满了书籍,需要调整书籍顺序python怎么写