首页 男生 其他 深入浅出React和Redux

10.3.2 Todo应用动画

深入浅出React和Redux 程墨 6426 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  10.3.2 Todo应用动画

  我们在Todo应用中使用react-motion来实现添加和删除待办事项的动画,可以和上一节的TransitionGroup对比一下,体会两者不同。

  相关代码可以在本书的Github代码库https://github.com/mocheng/react-and-redux/的目录chapter-10/todo_react_motion下找到。

  和前面章节中的Todo应用差别只有src/todos/views/todoList.js文件,而且这次我们不需要使用CSS文件,首先从react-motion库导入spring和TransitionMotion,代码如下:

  import {spring, TransitionMotion} from 'react-motion';

  spring函数用于产生动画属性的开始和结束状态,用于取代CSS3的transition方式;而TransitionMotion是一个组件,用于处理装载过程和卸载过程,对应于前文的TransitionGroup,但两者使用方式有较大差别。

  修改之后的TodoList组件的代码如下:

  const TodoList = ({todos}) => {

  const styles = getStyles(todos);

  return (

  {

  interpolatedStyles =>

  {

  interpolatedStyles.map(config => {

  const {data, style, key} = config;

  const item = data;

  return (

  text={item.text} completed={item.completed} />);

  })

  }

  }

  );

  };

  TransitionGroup应该直接包含需要动画效果的子组件,子组件可以是一组。例如,在Todo应用中,TransitionGroup直接包含多个TodoItem组件,TransitionGroup本身作为ul的子组件;但是,使用TransitionMotion就不可以这样,因为TransitionMotion只能包含函数作为子组件,而且这个函数只能返回一个元素,不能是一个数组作为元素,在上面的代码中我们可以看到,TranistionMotion不是ul的子组件,ul被移到了TranistionMotion的子组件函数内部,用于把所有TodoItem包裹为一个元素返回。

  TransitonMotion要求有一个名为styles的数组属性,这个数组的每个元素代表一个子组件。在Todo应用中,styles中每个元素包含显示一个子组件的所有样式和数据内容,这个styles数组的每个元素都是一个对象,包含key和style两个必有的属性,还有一个可选的data属性,获取styles的函数如下:

  const getStyles = (todos) => {

  return todos.map(item => ({

  key: item.id.toString(),

  data: item,

  style: {

  height: spring(60),

  opacity: spring(1)

  }

  })

  }

  其中,key属性是React要求动态数量的子组件必须包含的属性,在第5章对key属性有详细介绍,在Todo应用中,我们将其设为待办事项的id。不过,这个属性要求是一个字符串类型,所以我们用toString将id转为字符串。

  至于style属性,就是子元素动画的“目标状态”,我们利用spring函数指定了每个TodoItem组件的动画状态,含义是“以默认的刚性和阻尼设定,把height属性变成60,把opacity属性变成1:

  style: {

  height: spring(60),

  opacity: spring(1)

  }

  data属性的内容和动画无关,是子组件才理解的数据,react-motion对这个数据也并不关心,只是扮演一个搬运工的角色,把data传递给内层的函数。

  注意,styles属性的每个元素的所有值都可能不同,只是在Todo应用中,每个TodoItem只有key和data不同,而style都是相同的。

  在这里我们再次感受到styles命名的尴尬,虽然名为styles,却包含了key和data这样和样式无关的数据,真正和样式相关的只有style一个字段而已。

  在style中定义的只是动画的“目标状态”,但是要产生画面的运动,还需要定义动画的“初始状态”,在TransitonMotion中定义“初始状态”的是willEnter。

  在我们的例子中,willEnter函数代码如下:

  const willEnter = () => {

  return {

  height: 0,

  opacity: 0

  };

  };

  TransitionMotion延续了TransitionGroup的命名习惯,用enter代表一个新的组件“加入”到TransitionMotion之中,leave代表一个子组件“离开”TransitionMotion被卸载。

  willEnter属性的值是一个函数,当一个组件“加入”TransitionMotion的时候,TransitionMotion就调用这个函数,获得这个新组件的动画初始状态。在Todo应用中,TodoItem的初始状态在willEnter函数中定义,返回的内容让height和opacity属性都为0。于是,TransitionMotion就让新“加入”的组件展示height从0到60而opacity从0到1的动画过程。

  willLeave属性的值同样是一个函数,返回当一个组件“离开”TransitionMotion时的“结束状态”。注意,在“离开”的过程开始的时候,可以认为“进入”过程完毕了,这时候TodoItem的状态已经是style中定义的最终状态,所以“离开”动画过程的“初始状态”实际上就是“进入”状态的结束状态。要形成动画效果,willLeave返回的结果就不能像willEnter那样只是返回纯数字的结果,而是要用spring明确动画的节奏,在代码中我们可以看到willEnter和willLeave两个函数返回结果的不同。

  我们的例子中对应的willLeave函数代码如下:

  const willLeave = () => {

  return {

  height: spring(0),

  opacity: spring(0)

  };

  }

  对比TransitionGroup,可以看到TransitonMoton不需要CSS的协助,完全通过Java-Script代码就可以实现动画的效果。

  读者可能会问,在TransitionGroup中有一个appear定制第一次装载过程的动画,那么在TransitionMotion中如何定制这个过程呢?

  TransitionMotion还支持一种属性叫defaultStyles,可以满足类似的要求,default-Styles的格式类似styles,也是一个数组,只是每个数组元素中的style字段代表的就是第一次加载时的样式。

  读者可以尝试给Todo应用的TransitonMotion加上下面的defaultStyles属性值,代码如下:

  const defaultStyles = todos.map(item => {

  return {

  key: item.id.toString(),

  data: item,

  style: {

  height: 0,

  opacity: 0

  }

  };

  });

  最终的效果就是刷新网页之后,所有预先存在的待办事项都以动画方式出现。

  在这里只介绍了React-Motion很小的一部分功能,也就是用TransitionMotion管理装载和卸载React组件的动画功能,实际上React-Motion的功能远不止于此。在React-Motion中,还有一个组件Motion提供普通的动画功能,一个组件StaggeredMotion提供固定数量组件相互依赖的动画展示功能,和TransitionMotion一样,React-Motion提供的这些组件都遵循“以函数为子组件”的模式,只要掌握了“以函数为子组件”模式,就理解了全部React-Motion的原则。 深入浅出React和Redux

目录
设置
手机
书架
书页
评论