What's a React.Js-Friendly Way to Animate a List-Reordering

What's a react.js-friendly way to animate a list-reordering?

I just released a module to tackle exactly this problem

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

It does a few things differently than Magic Move / Shuffle:

  1. It uses the FLIP technique for hardware-accelerated 60FPS+ animations
  2. It offers options to "humanize" the shuffle by incrementally offsetting delay or duration
  3. It handles interruptions gracefully, no weird glitch effects
  4. Bunch of other neat stuff like start/finish callbacks

Check out the demos:

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

Animate when items in a list change order with React?

In react-spring there is an example about it. But it is quite complicated, there is a lot happening. I created a simplified version from it.

You have an array of names. You define a y value based on the index. And you can move the elements with translate property. The position is set to absolute.

With one click you can shuffle the array. With another click you can remove elements. In react transition, you can define the entering and leaving animation. The leaving animation called when you remove an element.

import { render } from 'react-dom';
import React, { useState } from 'react';
import { useTransition, animated } from 'react-spring';
import shuffle from 'lodash/shuffle';
import './styles.css';

let data = [
{
name: 'Rare Wind'
},
{
name: 'Saint Petersburg'
},
{
name: 'Deep Blue'
},
{
name: 'Ripe Malinka'
},
{
name: 'Near Moon'
},
{
name: 'Wild Apple'
}
];

function App() {
const [rows, set] = useState(data);
let height = 20;
const transitions = useTransition(
rows.map((data, i) => ({ ...data, height, y: i * height })),
d => d.name,
{
from: { position: 'absolute', height: 20, opacity: 0 },
leave: { height: 0, opacity: 0 },
enter: ({ y, height }) => ({ y, height, opacity: 1 }),
update: ({ y, height }) => ({ y, height })
}
);

return (
<div class="list" style={{ height }}>
<button onClick={() => set(shuffle(rows))}>click</button>
<button onClick={() => set(rows.slice(1))}>remove first</button>
{transitions.map(({ item, props: { y, ...rest }, key }, index) => (
<animated.div
key={key}
class="card"
style={{
zIndex: data.length - index,
transform: y.interpolate(y => `translate3d(0,${y}px,0)`),
...rest
}}
>
<div class="cell">
<div class="details">{item.name}</div>
</div>
</animated.div>
))}
</div>
);
}

const rootElement = document.getElementById('root');
render(<App />, rootElement);

Here is the sandbox: https://codesandbox.io/s/animated-list-order-example-with-react-spring-teypu

Edit: I added add element as well, because it is a nicer example this way. :)

Update: This sandbox is from necrifede who updated my example to version 9.:
https://codesandbox.io/s/animated-list-order-example-with-react-spring-forked-nhwqk9?file=/src/index.js

How to animate reorder with React Native FlatList?

The flatlist is sorted in order of the array that you feed it through the data prop. Whether you are using component-level state (i.e. this.state ) or redux to manage your state, you can reorder the array of elements and it should rerender in that order. For example, one implementation may be:

const checkedData = [...this.state.data].filter(item => item.checked === true);
const uncheckedData = [...this.state.data].filter(item => item.checked !== true);
const sortedData = checkData.concat(uncheckedData);
this.setState({ data: sortedData });

The javascript filter function should preserve original sort order of the subset.

To animate it, you can then look into LayoutAnimation in react native. It will apply an animation type to every change in your view between renders.

How to specify the order of items in ReactCSSTransitionGroup?

The ReactCSSTransitionGroup just animates changes in DOM, it doesn't care about order. Your state changes from odd to even numbers, there is no moment when it contains sorted array with all the numbers. You can work around it by modifying state in different way, temporary saving old items for animation purposes, something like that:

switch: function() {
var newItems;
if (this.state.items[0] % 2 !== 1) {
newItems = [1, 3, 5];
}
else {
newItems = [2, 4];
}
this.setState({
items: newItems,
previousItems: this.state.items
}, function() {
this.setState({
previousItems: []
})
});
}

After that, you need to modify your render method:

render: function() {
var currentItems = this.state.items.concat(this.state.previousItems).sort();
var items = currentItems.map(function(item, i) {
return (
<div key={item}>
{item}
</div>
);
}.bind(this));
return (
<div>
<button onClick={this.switch}>Switch</button>
<ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={1000} transitionLeaveTimeout={1000}>
{items}
</ReactCSSTransitionGroup>
</div>
);
}

Here is an updated fiddle: http://jsfiddle.net/ny5La5ky/

drag to reorder and save state with reactjs

I have fixed it,
here is the working url to play with https://codesandbox.io/s/quizzical-colden-rm62y

You were correct in guessing that the problem was with the onSortEnd function. Instead of swapping the newIndex and oldIndex position we just need to either bubble them up or down.

Here is a working code, it can be cleaned up a bit, but you got the idea :)

const onSortEnd = ({ oldIndex, newIndex }) => {
setUsers(prevState => {
const newItems = [...prevState];

if (oldIndex > newIndex) {
for (let i = oldIndex - 1; i >= newIndex; i--) {
newItems[i].order++;
newItems[oldIndex].order = newIndex;
}
} else if (oldIndex < newIndex) {
for (let i = oldIndex + 1; i <= newIndex; i++) {
newItems[i].order--;
newItems[oldIndex].order = newIndex;
}
}
return newItems.sort((a, b) => a.order - b.order);
});
};

Hope it helps. Happy coding :)

ReactJS component: fadeIn / fadeOut on mouse-enter / mouse-leave

Something like this?

http://plnkr.co/edit/5tLWNTtpsSWBUCgnKwQO?p=preview

I updated the state to have a hovered attribute and two mouse enter / leave methods to set the value which are bound the .range-picker-selection element in the render function

<div className="range-picker-selection" onMouseEnter={this.onMouseEnterHandler} onMouseLeave={this.onMouseLeaveHandler}>

onMouseEnterHandler: function () {
this.setState({
hovered: true
})
},
onMouseLeaveHandler: function () {
this.setState({
hovered: false
})
},

I also updated the render function to set the opacity to 0 or 1 accordingly for the elements.

style={{opacity: this.state.hovered ? 1 : 0}}

Finally, the actual animation is done with CSS animations

transition: opacity 1s;

I hope this helps



Related Topics



Leave a reply



Submit