💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 介绍 一个简单的 loading 动画或者页面切换效果不仅能缓解用户的等待情绪,甚至通过使用品牌 logo 等形式,默默达到品牌宣传的效果。 传统 web 动画大多数都通过直接操作实际 DOM 元素来实现,这在 React 中显然是不被提倡的。那么,在 React 中动画都是如何实现的呢? 在 React 中实现动画本质上与传统 web 动画一样,仍然是两种方式: 通过 css3 动画实现和通过 js 修改元素属性。只不过在具体实现时,要更为符合 React 的框架特性,可以概括为几类: 1. 基于定时器或 `requestAnimationFrame(RAF)` 的间隔动画; 2. 基于 css3 的简单动画; 3. React 动画插件 `ReactCSSTransitionGroup` ; 4. 结合 hook 实现复杂动画; 5. 其他第三方动画库。 # 基于`requestAnimationFrame(RAF)` ``` // 使用requestAnimationFrame改变state this.state= { percent: 10 }; ... increase = () => { const percent = this.state.percent; const targetPercent = percent >= 90 ? 100 : percent + 10; const speed = (targetPercent - percent) / 400; let start = null; const animate = timestamp => { if (!start) start = timestamp; const progress = timestamp - start; const currentProgress = Math.min( parseInt(speed * progress + percent, 10), targetPercent ); this.setState({ percent: currentProgress }); if (currentProgress < targetPercent) { window.requestAnimationFrame(animate); // 递归调用自身,直到满足条件 } }; window.requestAnimationFrame(animate);  }; ... ``` # Framer Motion 现已升级为:[Framer Motion](https://github.com/framer/motion) - Creating our first animation with React pose ` npm install react react-dom react-pose styled-components` # CSSTransitionGroup - Animating a todo list with `npm install react-transition-group` `react-transition-group`包含`CSSTransitionGroup`和`TransitionGroup`两个动画插件,其中,后者是底层 api,前者是后者的进一步封装,可以较为便捷地实现 css 动画。 ``` import React, { Component } from 'react'; import { CSSTransitionGroup } from 'react-transition-group'; ... render() { const renderTabs = () => { return tabData.map((item, index) => { return ( <div className={`tab-item${item.id === activeId ? ' tab-item-active' : ''}`} key={`tab${item.id}`} > {item.panel} <span className="btns btn-delete" onClick={() => this.deleteTab(item.id)}>✕</span> </div> ); }) } return ( <div> <div className="tabs" > <CSSTransitionGroup transitionName="tabs-wrap" transitionEnterTimeout={500} transitionLeaveTimeout={500} > {renderTabs()} </CSSTransitionGroup> <span className="btns btn-add" onClick={this.addTab}>+</span> </div> <div className="tab-cont"> cont </div> </div> ); ... ``` 使用`CSSTransitionGroup`需要注意以下几点: * `CSSTransitionGroup`默认在 DOM 树中生成一个`span`标签包裹其子节点,如果想要使用其他 html 标签,可设定`CSSTransitionGroup`的`component`属性; * `CSSTransitionGroup`的子元素必须添加`key`值才会在节点发生变化时,准确地计算出哪些节点需要添加入场动画,哪些节点需要添加离场动画; * `CSSTransitionGroup`的动画效果**只作用于直接子节点,不作用于其孙子节点**; * 动画的结束时间不以 css 中 transition-duration 为准,而是以`transitionEnterTimeout`,`transitionLeaveTimeout`,`TransitionAppearTimeout`为准,因为某些情况下 transitionend 事件不会被触发,详见[MDN transitionend](https://developer.mozilla.org/en-US/docs/Web/Events/transitionend)。 ## 结合 hook 实现复杂动画 CSSTransitionGroup 的底层 API `TransitonGroup` 还为其子元素额外提供了一系列特殊的生命周期 hook 函数,在这些 hook 函数中结合第三方动画库可以实现丰富的入场、离场动画效果。 TransisitonGroup 分别提供一下6个生命周期 hook 函数: 1. componentWillAppear(callback) 2. componentDidAppear() 3. componentWillEnter(callback) 4. componentDidEnter() 5. componentWillLeave(callback) 6. componentDidLeave() # 其他相关库 * [react-animations](https://github.com/FormidableLabs/react-animations) * [React-Spring](https://github.com/pmndrs/react-spring) * [https://github.com/animatedjs/animated](https://github.com/animatedjs/animated) * [https://github.com/chenglou/react-motion](https://github.com/chenglou/react-motion) 此外,还有很多优秀的第三方动画库,如 [GASP](https://greensock.com/gsap)、[react-motion](https://github.com/chenglou/react-motion) ,[Animated](https://github.com/animatedjs/animated), [velocity-react](https://github.com/google-fabric/velocity-react),[react-web-animation](https://github.com/bringking/react-web-animation)等,这些动画库在使用时也各有千秋。 # 结语 当我们在 React 中实现动画时,首先要考量动画的难易程度和使用场景,对于简单动画,优先使用 css3 实现,其次是基于 js 的时间间隔动画。如果是元素入场动画和离场动画,则建议结合 CSSTransitionGroup 或者 TransitionGroup 实现。当要实现的动画效果较为复杂时,不妨尝试一些优秀的第三方库,打开精彩的动效大门。 # 参考 [React 中常见的动画实现方式](https://tech.youzan.com/react-animations/) [创建 React 动画的五种方式](https://zhuanlan.zhihu.com/p/28536964)