10.2.2 ReactCSSTransitionGroup规则
您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!
10.2.2 ReactCSSTransitionGroup规则
从上面Todo的应用不难看出,TransitionGroup并不能代替CSS。恰恰相反,它离不开CSS,其扮演的角色是让React组件在生命周期的特定阶段使用不同的CSS规则,而连接React组件和CSS需要遵守一些规则。
1.类名规则
配合TransitionGroup中的transitionName属性,对应的CSS规则中类名遵从统一的规则。再类名由-符号把几个单词连接起来,除了transitionName的值,还可以有这几个单词:enter代表“装载”开始时的状态,leave代表“卸载”开始时的状态,active代表动画结束时的状态。
假设transitionName为sample,那么定制相关React组件的类名就是:
·sample-enter
·sample-enter-active
·sample-leave
·sample-leave-active
其中active后缀类名的作用比较特殊,因为用CSS3的transition功能实现动画,必须定义“开始状态”和“结束状态”,只有存在这两个状态,CSS3才知道如何将元素属性从“开始状态”在指定的时间按照指定的速度曲线转化为“结束状态”,这两个状态必须定义在两个不同的CSS类中,否则CSS3无法区分。例如,首先给一个元素名为sample-enter的CSS类,然后再给它一个名为sample-enter-active的CSS类,因为这两个类分两次赋予这个元素,CSS3就能够发现两者的差别,以transition的方式动画显示这个转化过程,这也就是为什么对于sample-enter需要一个sample-enter-active类,而sample-leave需要一个sample-leave-active的原因,-active后缀的类代表的就是动画结束的状态。
在Todo应用的例子中,当TransitionGroup的任何一个子组件(在这个例子中就是TodoItem组件)被装载时,这个组件会被加上两个类名,fade-enter和fade-enter-active。这两个类并不是同时加上去的,React会先让TodoItem先具有fade-enter类,然后在JavaScript下一个时钟周期才加上fade-enter-active类,分两次进行,这样才会产生从fade-enter到fade-enter-active描述的样式的转化过程,结果就是让TodoItem组件在500毫秒内以ease-in的速度曲线将不透明度从0.01变成1,这就是一个淡入的效果。
当TransitionGroup的某个子组件被删除卸载时,这个组件会被先后加上两个类名,fade-leave和fade-leave-active,这两个类的会让TodoItem组件在200毫秒内以ease-in的速度曲线将不透明度从1变成0.01,然后才彻底删除,这就是一个淡出的效果。
2.动画时间长度
使用TransitionGroup,动画持续的时间在两个地方都要指定,第一个是在Transition-Group中以Timeout为结尾的属性,比如transitionEnterTimeout和transitionLeave-Timeout,第二个地方是在CSS文件中的transition-duration规则。
一般来说,这两处地方的时间应该是一致的,不过我们先搞清楚为什么要分两处来指定动画时间。
以Todo应用中的enter过程为例,transitionEnterTimeout值为500是告诉Transition-Group每个新加入的TodoItem组件的动画时间持续500毫秒,那样TransitionGroup会在这个动画开始之初给TodoItem组件加上fade-enter和fade-enter-active类。500毫秒之后,就会把这两个类删除掉,然后就是TodoItem正常的显示方式。
再看CSS中的规则,它只管在500毫秒内以指定节奏完成动画过程,因为真正的动画的时间是由CSS规则控制的。
假如两处的设定不一致,比如transitionEnterTimeout的值为250,CSS规则中依然是500ms,那么CSS依然会以500毫秒的时间节奏将TodoItem的不透明度从0.01变成1,但是,在进行到250毫秒的时候,transitionEnterTimeout的设定就会让.fade-enter和.fade-enter-active类被删掉,这时候TodoItem组件会一下子进入普通显示状态,也就是不透明度为1的状态,这样就会感觉到不透明度有一个明显的跳跃,而不是平滑过渡。
一般来说,没有用户体验会希望有这样的“突变”效果,所以,通常两处的设定必须一致。
当然,同一个值需要在两处设定,这明显是重复代码,这是TransitionGroup的一个缺点。
3.装载时机
读者可能会有一个疑问,为什么用TransitionGroup在todoList.js文件中包住所有TodoItem组件实例的数组,而不是让TransitionGroup在todoItem.js文件中包住单个TodoItem组件呢?
看起来应该能实现同样效果,但实际上这样做不行。因为TransitionGroup要发挥作用,必须自身已经完成装载了。这很好理解,TransitionGroup也只是一个React组件,功能只有在被装载之后才能发挥,它自己都没有被装载,怎么可能发挥效力呢?
假如在todoItem.js中使用TransitionGroup,也就是说把TransitionGroup当成了TodoItem组件的子组件,那么只有TodoItem完成装载时才被装载。如此一来,当一个TodoItem进入装载过程的时候,它内部的所有子组件还没有装载呢,包括Transition-Group,这样根本就不会有动画效果。
当我们要给一个数量变化的组件集体做动画的时候,TransitionGroup总是要包住这整个结合,就像TodoList上包住一个map函数产生的TodoItem组件实例数组一样,这也就是为什么TransitionGroup命名中带一个Group的原因。
4.首次装载
有一个有趣的现象,在Todo应用中,TransitionGroup自身被装载的时候,可能已经包含了若干个TodoItem组件实例,但是这些TodoItem组件实例虽然经历了装载过程,却没有动画效果,只有在TransitionGroup被装载之后新加入的TodoItem组件才有动画效果。
似乎类fade-enter和fade-enter-active中定义的CSS规则对于随TransitionGroup一同装载的TodoItem组件实例无效。这是因为enter过程并不包括TransitionGroup的首次装载,顾名思义,enter就是“进入”TransitionGroup,TransitionGroup实例装载完成之后,新加入的TodoItem组件算是“进入”,但是随TransitionGroup实例一起装载的TodoItem组件不算“进入”。
如果我们就想让随TransitionGroup实例一起装载的子组件也有动画呢?那就要使用appear过程,appear过程代表的就是随TransitionGroup一起“出现”的过程。
TransitionGroup的appear也有对应的transitionAppearTimout属性,对应的CSS类符合一样的模式,以-appear和-appear-active为结尾,对应Todo应用中的例子,就是fade-appear和fade-apppear-active类。
不过,appear还是有一点特殊,因为通常对于首次装载没有必要有动画,所以默认控制动画的开关是关闭的。所有的动画过程,包括active、leave和appear都有对应的TransitionGroup动画开关属性,分别是transitionEnter、tranisitionLeave和transition-Appear,这三个属性是布尔属性,前两个默认是true,但第三个transitionAppear的值默认是false。
为了启用这个过程的动画,我们要在TransitioGroup的属性中显示地设定transitionAppear的值为true,代码如下:
在todoItem.css中,我们可以让appear和enter具有一样的CSS规则:
.fade-enter,
.fade-appear {
opacity: 0.01;
}
.fade-enter.fade-enter-active,
.fade-appear.fade-appear-active{
opacity: 1;
transition: opacity 500ms ease-in;
}
在浏览器中刷新Todo应用网页,可以看到这时候现成的三个待办事项也是以渐入的方式出现的。当然,每次切换过滤器,TodoItem组件都以动画方式出现,看起来并不是很好的效果。
通常应用中不需要使用appear过程,这也就是为什么transitionAppear属性默认是false的原因。 深入浅出React和Redux