J'ai une liste d'éléments avec les scores, classés par scores, rendus par react.js sous forme d'une liste d'éléments rectangulaires orientés verticalement (les scores les plus élevés en haut). Les survols et autres clics sur les éléments individuels peuvent afficher/masquer des informations supplémentaires, en modifiant leur hauteur verticale.
De nouvelles informations arrivent qui modifient légèrement les scores, faisant en sorte que certains éléments soient mieux classés et d’autres plus bas après un nouveau tri. Je voudrais que les éléments animent simultanément à leurs nouvelles positions, plutôt que d'apparaître instantanément dans leurs nouveaux emplacements.
Existe-t-il une méthode recommandée dans React.js, éventuellement avec un add-on populaire?
(Dans une situation antérieure similaire utilisant D3, une technique que j'ai utilisée était approximativement:
Maintenant, j'espère un effet similaire d'une manière très réaliste ...)
Je viens de publier un module pour aborder exactement ce problème
https://github.com/joshwcomeau/react-flip-move
Il fait certaines choses différemment de Magic Move/Shuffle:
Découvrez les démos:
http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle
React Shuffle est solide et à jour. C'est inspiré par la démo Ryan Florences Magic Move
Je me rends compte qu’il s’agit d’une question un peu ancienne et que vous avez probablement déjà trouvé une solution, mais si vous avez une question, essayez la bibliothèque MagicMove de Ryan Florence. C'est une bibliothèque React pour gérer le scénario exact que vous décrivez: https://github.com/ryanflorence/react-magic-move
Voyez-le en action: https://www.youtube.com/watch?v=z5e7kWSHWTg#t=424
Edit: Ceci est cassé dans React 0.14. Voir Réagir Mélanger comme une alternative, comme suggéré par Tim Arney ci-dessous.
La meilleure façon dont je puisse penser à accomplir ceci spontanément serait tout d'abord de vous assurer que vous donniez à chacun de vos éléments une clé comme décrit ici:
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
Ensuite, je définirais une animation CSS3 similaire à celle-ci:
Essentiellement, dans votre fonction de rendu, vous calculeriez où l'élément est supposé aller, ReactJS placera l'élément là où il est supposé être (assurez-vous de donner à chaque élément la même clé à chaque fois! Il est important de vous assurer que ReactJS réutilise votre élément DOM correctement). En théorie du moins, le CSS devrait s'occuper du reste de l'animation pour vous en dehors de reactjs/javascript.
Disclamer ... Je n'ai jamais vraiment essayé ceci;)
Étant donné que la position d'un élément dépend fortement du moteur DOM, il est très difficile d'implémenter l'interpolation et l'animation de manière pure dans les composants React. Si vous voulez le faire proprement, je pense qu'il vous faut au moins: un magasin pour conserver la position actuelle d'un élément, un magasin pour conserver la position suivante d'un élément (ou le combiner avec le magasin précédent) et un rendu ( ) méthode qui applique les marges de décalage par élément jusqu’à ce que les positions suivantes deviennent actuelles dans la dernière image. Comment calculer les prochaines positions en premier lieu est également un défi. Mais étant donné que les éléments n’échangent que des positions, vous pouvez utiliser le magasin avec les positions actuelles pour échanger l’élément n ° 0 avec l’élément n ° 1, en échangeant leurs décalages dans le magasin suivant.
En fin de compte, il est plus facile d'utiliser une bibliothèque externe pour manipuler les marges des éléments après leur rendu.
Je ne souhaitais pas utiliser de bibliothèque tierce pour mes animations. J'ai donc implémenté la technique d'animation " FLIP " à l'aide des méthodes de cycle de vie de React, getSnapshotBeforeUpdate () et composantDidUpdate ().
Lorsque props.index de l'élément de liste change, l'ancienne position est capturée dans les méthodes getSnapshotBeforeUpdate () et composantDidUpdate (), la transformation css: translateY () est appliquée de sorte que l'élément semble s'animer d'ancienne position à la position actuelle.
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;
}