资讯专栏INFORMATION COLUMN

让React应用“动”起来

xiyang / 2131人阅读

摘要:因为其组件只是根据提供的及属性,生成动画的数据,业务应用中拿到生成的数据后根据需要添加需要动画的组件样式。除了上述简单的动画应用,在复杂动画的实现方面,表现非常优越。

WEB应用中动画很重要

不管是web应用还是原生应用,也不论是PC端应用还是移动端应用,动画都扮演了一个重要的角色。

尽管动画并不会添加应用的实际动能,但一个好的动画,一个流畅且优雅,选择在恰当时机出现的动画,能为应用增色不少,能很好的引导用户进行下一步操作,让应用的场景切换更合理。一个小小的细节动画,就能几个层次的提升应用的用户体验。

举个简单的例子,应用中最常见的页面间切换,如果缺少切换动画,那就会是这个样子:

非常生硬,页面的出现显得非常突兀。作为对比,我们可以看下添加了动画的页面切换是什么样子的呢:

增加了用户预期,也能较好的提示用户页面的层级关系。

再举个弹窗的例子,先放两个对比图,左边是无动画的效果,右边是添加完动画的效果:

动画虽然没有添加什么实际可见的功能,但是通过对比,不难发现,动画的添加,让弹窗的出现显得平滑自然,让页面的场景替换有一个过程,减少突兀感,让用户体验感受增色不少不是吗?

当然有可能因为录制的问题,动画效果不是很明显,你可能有不同的看法。
动画实现的基本原理

web应用的基本骨架是DOM,正是一个个的DOM节点,构建出web应用,换句话说,就是web应用呈现出来的样子是DOM决定的(当然这里把CSS样式,归纳为了DOM的一部分)。所以动画的实现,从本质上来讲,就是操作DOM:让DOM在不同的时间节点,在不同的位置、有不同的大小、透明度、呈现不同的背景色等,并且让这种变化连续起来,则构成了我们能观察到的动画。

基于web动画实现的基本原理,在我们直接操作DOM的时代,实现动画相对我们来说,非常直观——只要知道怎么操作DOM即可。比如需要实现一个黑色背景的div方块,1s内从离左边距100px的位置,移动到离左边距200px的位置,则我们只需要每秒控制该div的left值增加(200-100)/60px即可实现一个匀速的动画效果。怎么样,很简单吧。

上述动画实现的方式,我们称为JS动画。是由JS脚本逻辑,动态的改变DOM的CSS属性值。

CSS3中,添加了动画的实现的方案,所以web中第二种动画实现,被我们称为CSS动画。CSS动画,最主要的几个CSS属性是: transition,transform,animation,由于与本文的主题不是密切相关,此处就不做详细介绍,有兴趣可以自行搜索相关文章查阅。

MV*模式下的动画实现

这里的MV*模式我们不展开说,特指React中动画的实现。React由于加入了虚拟DOM等诸多特性,并且其开发模式让开发者不需要或者不推荐直接接触到真实的DOM结构。所以其动画实现,与常规的开发方式下的动画实现,存在一定得差异。

动画的实现最终一定是落地到操作DOM,MV*模式的框架则是数据驱动DOM的展示。如果我们因为动画实现的需要,对DOM的操作出现问题,势必会影响到应用的相关操作。我们先不去深入探讨怎么解决这个问题,先看看React官方和社区对这个问题是怎么解决的,让我们能先给我们的React应用添加上需要的动画。

由浅及深,我们先学会怎么使用,再去了解内部的实现原理,从而根据应用自身需求,能实现自定义的动画,能实现更为复杂的交互动画等。

React中我们怎么添加动画

React的动画库中,比较常用的是react-addons-css-transition-groupreact-addons-transition-group 以及react-motion 。其中, react-addons-css-transition-groupreact-addons-transition-groupHigh-Level API库,react-addons-css-transtion-group是基于react-addons-transition-group的上层封装。目前react-addons-css-transition-groupreact-addons-transition-group合并成一个库,叫react-transition-group

react-transition-group@v1.x版本中的API, 基本保持与两个多带带库的API形式一致,但@v2.x版本中的API变化较大,并不能完全切换,这个需要注意。本文的示例是以独立库,也就是类react-transtion-group@v1.xAPI提供的。

另一个常用的React动画库是react-motion 。该库拥有非常棒的特性,能够创建出非常细腻的动画,接着往下看,会介绍下基本的使用,然后参照其官方文档,相信可以实现出大多数你想要的动画的。

1. ReactCSSTransitionGroup

react-addons-css-transition-group,一般称其export的组件为ReactCSSTransitionGroup,它提供一种声明的方式来定义CSS动画ReactCSSTransitionGroup子组件必须大于或等于1个,不能为空。

ReactCSSTranstionGroup组件暴露的属性有:

transitionAppear/transitionEnter/transitionLeave: Boolean 类型,标识是否开启动画

transitionAppearTimeout/transitionEnterTimeout/transitionLeaveTimeout: 定义各阶段动画的时长

transitionName:自定义各阶段动画的 CSS 样式名

component: ReactCSSTranstion以什么组件包裹(wrap)子组件,默认为span,可以是React Element 。

我们以 todo-list 为例,看看ReactCSSTransitionGroup怎么使用。由于篇幅限制,todo-list 相关的业务代码忽略,可以在这里查看完整代码。以下是动画部分代码:

index.js:

import React , { Component } from "react" ;
import ReactCSSTranstionGroup from "react-addons-css-transition-group" ;

export default class App extends Component{
    ... ,
    
    render(){
        const { items } = this.state ;
        
        return (
            
... ,
{ items.map((item)=>(
{item}
)) }
) } }

style.css:

.example-enter {
    opacity: 0.01;
}

.example-enter.example-enter-active {
    opacity: 1;
    transition: opacity 500ms ease-in;
}

.example-leave {
    opacity: 1;
}

.example-leave.example-leave-active {
    opacity: 0.01;
    transition: opacity 300ms ease-in;
}

有两点需要注意:

每个子组件必须有key,这样ReactCSSTrasntionGroup才能正确的mountingunmounting子组件。

自定义的动画时长,需要与CSS样式中定义的动画时长对应上。

关于react-addons-css-transition-group,知乎这篇文章CSS 动画及其在 React 中的应用有较为详细的介绍。

2. ReactTransitionGroup

react-addons-transition-groupreact-addons-css-transition-grouplow-level API,其提供动画执行中需要的各生命周期函数:

componentWillAppear(): componentDidMount时执行, 渲染TransitionGroup时执行并且只会执行一次。

componentDidAppear(): 传给componentWillAppear的callback执行后执行。

componentWillEnter(): componentDidMount时执行,子组件添加进TransitionGroup时执行。

componentDidEnter(): 传给componentWillEnter的callback执行后执行。

componentWillLeave(): 子组件从TransitionGroup中移除时执行,Though the child has been removed, TransitionGroup will keep it in the DOM until callback is called.

componentDidLeave(): componentWillLeave的callback执行后执行。通常情况下与ComponentWillUnmount的时机一致。

那我们看看,同样以 todo-list 为例, ReactTransitionGroup需要怎么做呢?

index.js:

import React,{ Component } from "react" ;
import ReactTransitionGroup from "react-addons-transition-group" ;

class Item extends Component{
    
    // 获取组件真实DOM
    getRef(ref){
        this.ref=ref ;
    }
    
    componentWillEnter(callback){
        this.ref.classList.add("example-enter") ;
        setTimeout(()=>{
          callback() ;
        },500) ;
    }
    
    componentDidEnter(){
        this.ref.classList.add("example-enter-active") ;
    }
    
    componentWillLeave(){
        this.ref.classList.remove("example-enter","example-enter-active") ;
        this.ref.classList.add("example-leave-active","example-leave") ;
        setTimeout(()=>{
          callback() ;
        },300) ;
    }

    render(){
        const { text , onRemove } = this.props ;
        return (
            
{text}
) } } export class App extends Component{ ... , render(){ const { items } = this.state ; return (
... ,
{ items.map((item,i)=>( )) }
) } }

style.css:

.example-enter {
    opacity: 0.01;
}

.example-enter.example-enter-active {
    opacity: 1;
    transition: opacity 500ms ease-in;
}

.example-leave {
    opacity: 1;
}

.example-leave.example-leave-active {
    opacity: 0.01;
    transition: opacity 300ms ease-in;
}

相比于ReactCSSTransitionGroup,我们需要自己去控制组件的动画生命周期,增加了一定的复杂度,但是对自动以动画,又能提供更好的灵活度。可以根据业务场景,选择合适的Group,去实现我们的需求。

通常情况下,我们用ReactCSSTransitionGroup就能满足较多的业务场景了,并且从实现上会容易很多。

3. ReactMotion

react-motion提供了5个API接口:

spring: 动画生成方法

Motion: React 组件

StaggeredMotion: React 组件

TransitionMotion: React 组件

presets: spring方法的配置项

跟其他React动画库一样,react-motion也提供React组件去包裹需要动画的业务组件。其中:

Motion组件只接受一个children组件

StaggeredMotion组件接受一组children组件

TranstionMotion组件可以支持其children组件mountingunmounting定义动画

仍然是 todo-list 的例子,react-motion的实现也非常简单:

import React,{ Component } from "react" ;
import { Motion , spring } from "react-motion";

export default class App extends Component{
    ... ,
    
    render(){
        const { items } = this.state ;
        return (
            
... ,
{ items.map((item)=>{ return ( { interpolatingStyle => (
{item}
) }
) }) }
) } }

通过上述简单的代码,即可实现每个Item在mounting的时候渐现的效果。

另一方面,观察上述实现,我们不难发现,react-motion不仅仅支持React web应用,它应该也能轻松的应用到React-Native中。因为其React组件只是根据提供的defaultStylestyle属性,生成动画的数据,业务应用中拿到生成的数据后根据需要添加需要动画的组件样式。react-motion在web应用中性能表现较为可观,在React-Native应用中的性能表现,有待我们调研。

除了上述简单的动画应用,react-motion在复杂动画的实现方面,表现非常优越。下面的动图是react-motion实现的一个动画演示:

这个示例展示了部分react-motion的能力,更多关于react-motion的应用就让我们一起去发现吧。

结语

当然React动画相关的库还有很多,本文不过多介绍。通过上述对这些库的使用做简单介绍,笔者希望通过对它们实现进行分析,让读者能更好的理解与掌握,能对React动画的实现原理和实现方式,有更为清晰的认识。

但是对相关代码的研究,深入度还不足以给读者朋友分享,所以暂时留坑,后续会将相关源码的学习,记录在文档React动画的实现原理一文中,并计划添加从零开始,实现React动画文章作为学习成果。如果对React动画保有兴趣,可以关注这两篇文章的后续内容。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94656.html

相关文章

  • React Motion 缓函数剖析

    摘要:大家可以尝试使用的,配置一个合适的劲度系数和空气阻力。所做的事,只不过自己实现了一套缓动函数。 根据经典力学的观点,世界上所有的原子每时每刻仿佛都会根据当前速度、受力和位置计算出下一刻的速度、受力和位置。上帝有一台超级计算机吗?非也,反而计算机是我们利用原子的这些特性拼装出来的。现在,我们却要用计算机,像上帝那样,再造一个世界。 我不知道这个世界上有没有仿世学,但是既然动画是要模仿现实...

    wfc_666 评论0 收藏0
  • react进阶漫谈

    摘要:父组件向子组件之间非常常见,通过机制传递即可。我们应该听说过高阶函数,这种函数接受函数作为输入,或者是输出一个函数,比如以及等函数。在传递数据的时候,我们可以用进一步提高性能。 本文主要谈自己在react学习的过程中总结出来的一些经验和资源,内容逻辑参考了深入react技术栈一书以及网上的诸多资源,但也并非完全照抄,代码基本都是自己实践,主要为平时个人学习做一个总结和参考。 本文的关键...

    neuSnail 评论0 收藏0
  • react进阶漫谈

    摘要:父组件向子组件之间非常常见,通过机制传递即可。我们应该听说过高阶函数,这种函数接受函数作为输入,或者是输出一个函数,比如以及等函数。在传递数据的时候,我们可以用进一步提高性能。 本文主要谈自己在react学习的过程中总结出来的一些经验和资源,内容逻辑参考了深入react技术栈一书以及网上的诸多资源,但也并非完全照抄,代码基本都是自己实践,主要为平时个人学习做一个总结和参考。 本文的关键...

    wyk1184 评论0 收藏0
  • react进阶漫谈

    摘要:父组件向子组件之间非常常见,通过机制传递即可。我们应该听说过高阶函数,这种函数接受函数作为输入,或者是输出一个函数,比如以及等函数。在传递数据的时候,我们可以用进一步提高性能。 本文主要谈自己在react学习的过程中总结出来的一些经验和资源,内容逻辑参考了深入react技术栈一书以及网上的诸多资源,但也并非完全照抄,代码基本都是自己实践,主要为平时个人学习做一个总结和参考。 本文的关键...

    junnplus 评论0 收藏0
  • 使用 React Native 构建 Facebook Paper 类似的 UI

    摘要:我模仿的应用构建了一个开闭卡片的轮播效果作为技术演示它使用了及其动画库当人们听到后第一反应会觉得它运行缓慢这是因为一般人会去这样解释它允许你通过构建你的应用程序而人们会认为浏览器中运行的性能并不够好但事实是它采用的全部都是原生界面元素但你通 我模仿 Facebook 的 Paper 应用构建了一个开闭卡片的轮播效果作为技术演示.它使用了 React Native 及其动画库. 当人们听...

    justjavac 评论0 收藏0

发表评论

0条评论

xiyang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<