问题
原生应用和桌面应用通常使用动画来直观展示不同的元素。如果你单击一个列表中的一项,应用程序显示详细信息时有展开效果。应用程序也会使用向左滑动或向右滑动的交互效果来表示用户是接受还是拒绝了某个选项。
因此,动画也经常用来指示关注点的变化。在展示详情时可以使用放大显示的动画;引导用户浏览人员名单上的下一个人;或者在切换URL时使用匹配(matching)动画来反映变化。
但是在切换路由时,我们如何创建动画呢?
解决方案
在本解决方案中,我们需要react-router-dom库和react-transition-group库:
我们将为前面使用过的About组件 创建动画。About组件有两个选项卡,即People和Offices,分别对应的路由是 /about/people 和 /about/offices 。
当有人单击其中一个选项卡时,我们会先淡出旧选项卡的内容,然后再淡入新选项卡的内容。虽然我们使用了淡入淡出动画,但我们也可以进一步使用更复杂的动画,比如向左或向右滑动选项卡内容 。然而,一个简单的淡入淡出动画也可以更清晰地演示动画的实现原理。
在About组件中,选项卡的内容是在不同的路由中分别由People和Offices组件来展示的:
要为Switch组件内部的组件创建动画,我们需要做两件事:
·当地址发生变化时,可以追踪到
·当这种情况发生时,可以为选项卡的内容创建动画
我们怎么知道地址何时改变了?我们可以从react-router-dom的useLocation Hook中获取当前地址:
现在进入更复杂的任务:动画本身。接下来是一系列相当复杂的事件,但花点时间去理解它是值得的。
当使用动画从一个组件切换到另一个组件时,我们需要将两个组件都保留在页面上。当Offices组件淡出时,People组件将淡入 。我们可以通过将两个组件放在一个过渡组(transition group)中来做到这一点。过渡组是一组组件,这些组件有些正在展示,有些则正在消失。
我们在一个TransitionGroup组件中创建一个过渡组囊括我们的动画。我们还需要一个CSSTransition组件来协调CSS动画的细节。
我们更新的代码将Switch包裹在TransitionGroup和CSSTransition组件中:
注意,我们把location.key值传给CSSTransition组的key属性,然后将location值传给Switch组件。location.key是当前地址的散列值。将location.key值传给过渡组会保证CSSTranstion在动画完成前一直存在于虚拟DOM中。当用户单击其中一个选项卡时,会导航到一个新的地址,这将刷新About组件。TransitionGroup会将现有的CSSTransition保存在组件树中500毫秒。但TransitionGroup同时也将迎来第二个CSSTransition组件。
每个CSSTransition组件都将保持其子组件处于alive状态(如图2-10所示)。
图2-10:TransitionGroup将旧组件和新组件都保存在虚拟DOM中
我们需要将location值传递给Switch组件:旧的选项卡需要Switch组件,新的选项卡也需要Switch组件来展示路由跳转后的内容。
现在来看动画本身。CSSTransition组件接受一个名为classNames的属性,我们把它的值设置为fade。注意,classNames是复数,以区别于标准的className属性。
CSSTransition将使用classNames生成四个不同的类名:
·fade-enter
·fade-enter-active
·fade-exit
·fade-exit-active
fade-enter类用于组件将要淡入动画时的状态,fade-enter-active类用于组件正在进行淡入动画时的状态,fade-exit和fade-exit-active分别用于组件将要和正在进行淡出动画时的状态。
CSSTransition组件将把这些类名添加到它们的子元素中。如果关注从Offices选项卡切换到People选项卡的动画过程,CSSTransition会给People组件的容器DOM元素增加fade-enter-active类,然后给Offices组件的容器DOM元素增加fade-exit-active类。
剩下要做的就是定义CSS动画本身:
fade-enter-类使用CSS过渡将组件的不透明度从0逐渐变为1。fade-exit-类把不透明度从1逐渐变回到0。将动画类的实现保存在一个单独的CSS文件中会是一个好主意。这样我们就可以在其他地方重用它们。
动画完成了。当用户单击一个选项卡时,他们会看到内容从旧数据交叉淡变为新数据(如图2-11所示)。
图2-11:选项卡内容从Offices淡变为People
讨论
使用不当时,动画可能会很让人烦恼。你添加的每个动画都应该有特定目的。如果你只是为了增加吸引力而想添加动画的话,你一定会发现用户不喜欢它。一般来说,最好在添加动画之前问几个问题:
·这个动画有助于说明两个路由之间的关系吗?是使用放大动画来表示查看更多细节,还是滑动到一边查看一个相关项?
·动画时长多久比较合适?超过半秒可能就太长了。
·对性能有什么影响?如果浏览器将工作移交给GPU,CSS过渡通常效果甚微。但是在移动设备上的老旧浏览器中会发生什么呢?
你可以从GitHub网站( https://oreil.ly/UCu75 )下载本解决方案的源代码。