用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啦?那么接下来的实现可以参考这一篇文章(期待中)。

相关推荐
我会冲击波13 分钟前
vibe coding 一个多月,我把 Claude Code + Pi 做成了桌面 IDE,还给它移植了 VS Code 的编辑体验
前端·javascript·后端
JimmtButler33 分钟前
从 Java 到 JS:我终于把闭包想明白了
javascript·后端
常宇佳1 小时前
vue3 实战系列之无法识别vue组件
前端·javascript·vue.js
全栈项目管理程序猿1 小时前
ArcGIS JS 基础教程(12):视图截图 takeScreenshot
javascript
小棉花的ai跨境之旅1 小时前
Codex 子 Agent 分工实战:Sol 当军师、Luna 当搬砖工,单任务成本压到 $0.61
javascript
daols882 小时前
vue 实现基于 vxe-table 构建多维度产品对比表
前端·javascript·vue.js
前端 贾公子2 小时前
第08章:中间件(5)
服务器·前端·javascript
二级小助手3 小时前
二级Web前端选择题高频真题20道与考场避坑笔记
javascript·css3·html5·web前端·计算机二级·二级web·web真题
前端小卡拉4 小时前
用了大半年 AI 编程工具,我才搞懂 Skill 到底是什么
前端·javascript
郭邯4 小时前
用 Intl.DateTimeFormat 手写一个时区转换器,顺便聊聊我和 AI 结对编程的日常
前端·javascript