什么是对列表重新排序进行动画处理的 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 友好方式?的主要内容,如果未能解决你的问题,请参考以下文章