Javascript 什么是对列表重新排序进行动画处理的 react.js 友好方式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27198479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's a react.js-friendly way to animate a list-reordering?
提问by gojomo
I have a list of items with scores, ordered by scores, rendered by react.js as a vertically-oriented list of rectangular items (highest scores at top). Hovers and other clicks on the individual items may show/hide extra info, changing their vertical height.
我有一个带有分数的项目列表,按分数排序,由 react.js 呈现为垂直方向的矩形项目列表(最高分数在顶部)。对单个项目的悬停和其他点击可能会显示/隐藏额外信息,从而改变它们的垂直高度。
New information arrives that changes the scores, slightly, making some items rank higher and others lower after a re-sort. I'd like the items to simultaneously animateto their new positions, rather than appear in their new locations instantly.
新信息的到来会稍微改变分数,使一些项目在重新排序后排名更高,而其他项目排名更低。我希望这些物品同时动画到它们的新位置,而不是立即出现在它们的新位置。
Is there a recommended way to do this in React.js, perhaps with a popular add-on?
在 React.js 中是否有推荐的方法来做到这一点,也许是一个流行的附加组件?
(In a similar past situation using D3, a technique I've used was roughly:
(在过去使用 D3 的类似情况中,我使用的技术大致是:
- Display the list with item DOM nodes in their natural order, with relative positioning. With relative positioning, other small changes – CSS or JS-triggered – to individual items' extent shift others as expected.
- Mutate all the DOM nodes, in a single step, to have their actual relative-coordinates as new absolute coordinates – a DOM change that causes no visual change.
- Re-order the item DOM nodes, within their parent, to the new sort order – another DOM change that causes no visual change.
- Animate all nodes' top-offsets to their new calculated values, based on the heights of all preceding items in the new ordering. This is the only visually-active step.
- Mutate all item DOM nodes back to non-offset relative-positioning. Again, this causes no visual change, but the now-relatively-positioned DOM nodes, in the natural ordering of the underlying list, will handle internal hover/expand/etc style changes with proper shifting.
- 以自然顺序显示包含 item DOM 节点的列表,并具有相对定位。通过相对定位,其他小的变化——CSS 或 JS 触发的——到个别项目的程度会按预期移动其他项目。
- 在一个步骤中改变所有 DOM 节点,将它们的实际相对坐标作为新的绝对坐标——DOM 更改不会导致视觉变化。
- 将它们的父项 DOM 节点重新排序为新的排序顺序——另一个 DOM 更改不会导致视觉变化。
- 根据新排序中所有前面项目的高度,将所有节点的顶部偏移量设置为它们的新计算值。这是唯一视觉上活跃的步骤。
- 将所有项目 DOM 节点突变回非偏移相对定位。同样,这不会导致视觉变化,但是现在相对定位的 DOM 节点,按照底层列表的自然顺序,将通过适当的移动处理内部悬停/展开/等样式的变化。
Now I'm hoping for a similar effect in a React-ish way...)
现在我希望以 React-ish 的方式产生类似的效果......)
采纳答案by Joshua Comeau
I just released a module to tackle exactly this problem
我刚刚发布了一个模块来解决这个问题
https://github.com/joshwcomeau/react-flip-move
https://github.com/joshwcomeau/react-flip-move
It does a few things differently than Magic Move / Shuffle:
它与 Magic Move / Shuffle 有一些不同:
- It uses the FLIP technique for hardware-accelerated 60FPS+ animations
- It offers options to "humanize" the shuffle by incrementally offsetting delay or duration
- It handles interruptions gracefully, no weird glitch effects
- Bunch of other neat stuff like start/finish callbacks
- 它使用硬件加速 60FPS+ 动画的 FLIP 技术
- 它提供了通过逐步抵消延迟或持续时间来“人性化”洗牌的选项
- 它优雅地处理中断,没有奇怪的故障影响
- 一堆其他整洁的东西,如开始/结束回调
Check out the demos:
查看演示:
http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle
http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle
回答by Andru
I realise this is a bit of an old question and you've probably found a solution by now, but for anyone else coming across this question, check out the MagicMove library by Ryan Florence. It's a React library to handle the exact scenario you describe: https://github.com/ryanflorence/react-magic-move
我意识到这是一个老问题,您现在可能已经找到了解决方案,但是对于遇到此问题的其他人,请查看 Ryan Florence 的 MagicMove 库。这是一个 React 库来处理您所描述的确切场景:https: //github.com/ryanflorence/react-magic-move
See it in action: https://www.youtube.com/watch?v=z5e7kWSHWTg#t=424
看看它的实际效果:https: //www.youtube.com/watch?v=z5e7kWSHWTg#t=424
Edit: This is broken in React 0.14. See React Shuffleas an alternative, as suggested by Tim Arney below.
编辑:这在 React 0.14 中被破坏了。请参阅下面的 Tim Arney 建议的React Shuffle作为替代方案。
回答by Tim Arney
React Shuffle is solid and up to date. It's inspired by Ryan Florences Magic Move demo
React Shuffle 是可靠且最新的。它的灵感来自 Ryan Florences Magic Move 演示
回答by Chris
The best way I can think of to accomplish this off the top of my head would be to first make sure that you give every one of your elements a key as described here:
我能想到的最好的方法是首先确保您为每个元素提供一个键,如下所述:
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
Next I would define CSS3 animation similar to this:
接下来我将定义类似于此的 CSS3 动画:
Basically in your render function you would calculate where the element is suppose to go, ReactJS will place the element where it's suppose to be (make sure you give each element the same key every time! This is important to make sure ReactJS reuses your DOM element properly). In theory at least, the CSS should take care of the rest of the animation for you outside of reactjs/javascript.
基本上在您的渲染函数中,您将计算元素应该去的位置,ReactJS 会将元素放置在它应该在的位置(确保每次都为每个元素提供相同的键!这对于确保 ReactJS 重用您的 DOM 元素很重要适当地)。至少在理论上,CSS 应该为您处理 reactjs/javascript 之外的其余动画。
Disclamer... I've never actually tried this ;)
免责声明...我从来没有真正尝试过这个;)
回答by Boog
I didn't want to use a third-part library for my animations so I implemented the "FLIP" animation technique using React lifecycle methods getSnapshotBeforeUpdate() and componentDidUpdate().
我不想为我的动画使用第三方库,所以我使用 React 生命周期方法 getSnapshotBeforeUpdate() 和 componentDidUpdate()实现了“ FLIP”动画技术。
When the list item's props.index changes the old position is captured in getSnapshotBeforeUpdate() and in componentDidUpdate() the css transform: translateY() is applied so that the item appears to be animating from old position to current position.
当列表项的 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(${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;
}
回答by Rygu
Because the position of an element depends heavily on the DOM engine, I find it really hard to implement tweening and animating in a pure way within React components. If you want to do it cleanly, I think you need at least: a Store to hold the current position of an element, a Store to hold the next position of an element (or combine it with the previous store), and a render() method that applies the offset margins per element until in the final frame, the next positions become current. How you calculate the next positions in the first place is also a challenge. But given that the elements only swap positions, you could use the Store with the current positions to swap element #0 with element #1, by swapping their offsets in the next positions Store.
因为元素的位置在很大程度上取决于 DOM 引擎,所以我发现在 React 组件中以纯粹的方式实现补间和动画真的很困难。如果你想干净利落地做,我认为你至少需要:一个 Store 来保存元素的当前位置,一个 Store 来保存元素的下一个位置(或将其与前一个 store 结合),以及一个 render( ) 方法,该方法应用每个元素的偏移边距,直到在最后一帧中,下一个位置变为当前位置。您首先如何计算下一个位置也是一个挑战。但是考虑到元素只交换位置,您可以使用带有当前位置的 Store,通过交换它们在下一个位置 Store 中的偏移量来交换元素 #0 和元素 #1。
In the end it's easier to just use an external library to manipulate the elements' margins after they're rendered.
最后,在渲染后使用外部库来操作元素的边距会更容易。
回答by Jessie Schalles
Just use ReactCSSTransitionGroup. It's built into the React with Addons package and is perfect for this use case! Just make sure that the component on which you apply the transition has already mounted to the DOM. Use the CSS that is in the linked example for a nice fading transition.
只需使用ReactCSSTransitionGroup。它内置在 React with Addons 包中,非常适合这个用例!只需确保应用过渡的组件已经安装到 DOM。使用链接示例中的 CSS 进行漂亮的淡入淡出过渡。

