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

相关推荐
DsirNg6 分钟前
Vue 3:我在真实项目中如何用事件委托
前端·javascript·vue.js
克喵的水银蛇6 分钟前
Flutter 适配实战:屏幕适配 + 暗黑模式 + 多语言
前端·javascript·flutter
冬男zdn9 分钟前
Next.js 16 + next-intl App Router 国际化实现指南
javascript·typescript·reactjs
有意义28 分钟前
this 不是你想的 this:从作用域迷失到调用栈掌控
javascript·面试·ecmascript 6
否子戈1 小时前
做中国人自己的视频编辑UI框架,WebCut正式开源
前端框架·音视频开发·视频编码
风止何安啊1 小时前
别被 JS 骗了!终极指南:JS 类型转换真相大揭秘
前端·javascript·面试
拉不动的猪1 小时前
深入理解 Vue keep-alive:缓存本质、触发条件与生命周期对比
前端·javascript·vue.js
over6971 小时前
深入理解 JavaScript 原型链与继承机制:从 instanceof 到多种继承模式
前端·javascript·面试
烂不烂问厨房1 小时前
前端实现docx与pdf预览
前端·javascript·pdf
GDAL1 小时前
Vue3 Computed 深入讲解(聚焦 Vue3 特性)
前端·javascript·vue.js