用react-flip-move给list加动画

今日需求来咯:给一个垂直排列的list组件,在变换顺序期间,有个过渡效果,最终实现像这样:

开始之前,我对比了市面上比较主流的几个工具库react-flip-movereact-spring,和react-motion

  • react-flip-move 用法稍微简单一点,不过只能适用于list,grid这一类的排列类型的组件。
  • react-spring 很全面的动画库,当然用起来也很复杂,有个springConfig,用来调节动画的参数,学习成本很高。
  • react-motionreact-spring封装后做了简化,只是用起来还是比react-flip-move要复杂,但适合各种需求,覆盖面很广。

我这个动画是加载可拖拽list上的,因为那个draggable list是用react-dnd实现的(《用react-dnd做一个可拖拽排列的list》),考虑到react-flip-move能更好地和react-dnd结合,我就选用了react-flip-move。如果你的前端项目有很多各种各样的动画需求,还是推荐你用react-motionreact-spring

react-flip-move官方文档中的示例是用react class component实现的,这篇文章我将仿照官网中Shuffle.tsx介绍如何用函数式组件实现,尤其是ref的部分会不太一样。

ListItem

首先画出ListItem组件,后期可能允许使用者传入自己的children,目前我先做出最简单的形式:

tsx 复制代码
// ListItem.tsx
import React from 'react';

type Props = {
  id: number;
  index: number;
  text: string;
}

const ListItem = (props: Props) => {
  const { id, index, text } = props;
  const listClass = `list-item card list`;
  const style = { zIndex: 100 - index, border: '2px blue solid', height: 30 };

  return (
    <li id={id} className={listClass} style={style}>
      {text}
    </li>
  );
};

export default ListItem;

SortableList

SortableListListItem的父组件,我先mock了一点数据(PETS),然后在父组件中引用flip-move

tsx 复制代码
// SortableList.tsx
import shuffle from 'lodash/shuffle';
import React, { useState } from 'react';
import FlipMove from 'react-flip-move';
import ListItem from './components/listItem/ListItem2';


const PETS = [
  { id: 1, name: 'dog' },
  { id: 2, name: 'cat' },
  { id: 3, name: 'fish' },
  { id: 4, name: 'hamster' },
]

const SortableList = () => {
  const [pets, setPets] = useState(PETS)

  const shufflePets = () => {
    setPets(prev => shuffle(prev));
  }
  
  return (
    <div
      style={{
        position: 'relative',
        width: '600px',
        height: '400px',
        border: '2px aqua solid'
      }}
    >
      <button onClick={shufflePets}>Shuffle</button>
      <FlipMove
        staggerDurationBy="30"
        duration={500}
        enterAnimation="accordionVertical"
        leaveAnimation="accordionVertical"
        typeName="ul"
      >
        {pets.map((pet, index) => (
          <ListItem
              key={pet.id}
              id={pet.id}
              index={index}
              text={pet.name}
          />
        ))}
      </FlipMove>
    </div>
  );
}

export default SortableList;

这样我们在页面上可以看到这样的list,点了shuffle按钮后,顺序就重新随机排列,这是一瞬间的变化,没有任何过渡效果。

moveListItem动画(shuffle)

于class component不同的是,在functional component中,我们需要将目标组件加上ref,让flip-move能get到那个element,才能加上动画。

tsx 复制代码
// ListItem.tsx
import React, { forwardRef } from 'react';

type Props = {
  id: number;
  index: number;
  text: string;
}

const ListItem = forwardRef((props: Props, ref) => {  // <--- use 'forwardRef' to connect ref
  const { id, index, text } = props;
  const listClass = `list-item card list`;
  const style = { zIndex: 100 - index, border: '2px blue solid', height: 30 };

  return (
    <li id={id} className={listClass} style={style} ref={ref}>
      {text}
    </li>
  );
});

export default ListItem;

这样就实现了我们想要的效果了,是不是很简单?

延展 - FLIP

这里要注意flip-move,顾名思义,flip是这个动画实现的本质。

  • ffirst,即在一个动画周期中组件的初始状态。
  • llast,即组件的最终状态。
  • iinvert,意思是反方向推断出一个变化,比如初始状态到最终状态是向下移动90px,那么inverttransform: -90px,方向是反的,这样让最终状态的组件看上去是在初始状态一样。
  • pplay,在transition后,invert的效果会被removed,这样整个过程看上去像是,从first状态到last状态,实际上是"虚假"的first状态(inverted from last)到last状态。

结尾

我在上一篇文章介绍了如何通过拖拽改变list的顺序(《用react-dnd做一个可拖拽排列的list》),如果加上这篇文章介绍的动画库,我们是不是可以做一个很丝滑的draggable list啦?那么接下来的实现可以参考这一篇文章(期待中)。

相关推荐
英宋2 分钟前
ckeditor5的研究 (2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件
前端·javascript
古夕2 分钟前
搞定滚动穿透
前端·javascript
英宋2 分钟前
ckeditor5的研究 (3):初步使用 CKEditor5 的 事件系统 和 API
前端·javascript
Danta7 分钟前
从 0 开始学习 Three.js(2)😁
前端·javascript·three.js
就是我12 分钟前
开发“业务组件库”,该从哪里入手?
前端·javascript·面试
Mintopia14 分钟前
在数字画布上雕刻曲线:NURBS 的奇幻冒险之旅
前端·javascript·计算机图形学
Mintopia22 分钟前
Three.js 力导向图:让数据跳起优雅的华尔兹
前端·javascript·three.js
晓得迷路了1 小时前
栗子前端技术周刊第 84 期 - Vite v7.0 beta、Vitest 3.2、Astro 5.9...
前端·javascript·vite
独立开阀者_FwtCoder1 小时前
最全301/302重定向指南:从SEO到实战,一篇就够了
前端·javascript·vue.js
Moment1 小时前
给大家推荐一个超好用的 Marsview 低代码平台 🤩🤩🤩
前端·javascript·github