在2019年为React应用制作动画的5种绝佳方法



React应用程序中的动画是一个热门话题。 事实是有很多创建它的方法。 一些开发人员通过向HTML类添加标签来使用CSS。 好的方法,值得使用。 但是,如果您想使用复杂类型的动画,则应该花时间研究GreenSock,它是一个流行且功能强大的平台。 还有许多用于创建动画的库和组件。 让我们谈谈他们。

本文讨论了为React应用程序设置动画的五种方法:

  • 的CSS
  • ReactTransitionGroup;
  • 反应动画
  • 反应揭示;
  • TweenOne和蚂蚁设计。

Skillbox建议:在线教育课程“专业Java开发人员”
我们提醒您: 对于所有“ Habr”读者来说,使用“ Habr”促销代码注册任何Skillbox课程时均可享受10,000卢布的折扣。

所有示例都可以在资源库中找到 (从此处将源插入到文章中,而不是像原始文章中那样插入图片)。

的CSS


在一开始就提到了这种方法,这确实很好。 如果汇编程序很小,而不是导入JavaScript库并使用它,则浏览器将不需要很多资源。 当然,这会影响应用程序性能。 如果您的动画应该相对简单,请注意此方法。

一个示例是动画菜单:



它相对简单,具有CSS属性和HTML标记的触发器,例如className =“ is-nav-open”。

有几种使用此方法的方法。 例如,在导航上创建包装器,然后调用字段更改。 由于导航具有250px的恒定宽度,因此具有margin-left或translateX属性的包装器的宽度应相同。 如果需要显示导航,请为包装器添加className =“ is-nav-open”并将包装器移至margin-left / translateX:0;。

最终,动画源将如下所示:

export default class ExampleCss extends Component { handleClick() { const wrapper = document.getElementById('wrapper'); wrapper.classList.toggle('is-nav-open') } render() { return ( <div id="wrapper" className="wrapper"> <div className="nav"> <icon className="nav__icon" type="menu-fold" onClick={() => this.handleClick()}/> <div className="nav__body"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Beatae ducimus est laudantium libero nam optio repellat sit unde voluptatum? </div> </div> </div> ); } } 

这是CSS样式:

 .wrapper { display: flex; width: 100%; height: 100%; transition: margin .5s; margin: 0 0 0 -250px; } .wrapper.is-nav-open { margin-left: 0; } .nav { position: relative; width: 250px; height: 20px; padding: 20px; border-right: 1px solid #ccc; } .nav__icon { position: absolute; top: 0; right: -60px; padding: 20px; font-size: 20px; cursor: pointer; transition: color .3s; } .nav__icon:hover { color: #5eb2ff; } 

我再说一遍,如果动画相对简单,那么这种方法就是主要的方法。 用户将欣赏浏览器的性能。

ReactTransitionGroup


ReactTransitionGroup组件是由ReactJS社区团队开发的。 有了它,您可以轻松实现基本的CSS动画和过渡。

ReactTransitionGroup旨在在更改组件生命周期时更改类。 它的尺寸很小,需要将其安装在React应用程序的软件包中,这会稍微增加整体组装的尺寸。 此外,您可以使用CDN。

ReactTransitionGroup具有三个元素,分别是Transition,CSSTransition和TransitionGroup。 要启动动画,您需要将组件包装在其中。 反过来,样式需要用CSS类编写。

这是动画,然后是实现它的方法。



第一步是从react-transition-group导入CSSTransitionGroup。 之后,您需要包装列表并设置transitionName属性。 每次在CSSTransitionGroup中添加或删除子项时,它都会获得动画样式。

 <CSSTransitionGroup transitionName="example"> {items} </CSSTransitionGroup> 

设置transitionName =“ example”属性时,样式表中的类必须以示例名称开头。

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

上面是使用ReactTransitionGroup的示例。

您还需要逻辑和两种方法来实现添加联系人列表的示例。

第一个handleAdd方法-它添加新的联系人,获得一个随机名称,然后将其放入state.items数组中。

要通过state.items数组中的索引删除联系人,请使用handleRemove。

 import React, { Component, Fragment } from 'react'; import { CSSTransitionGroup } from 'react-transition-group' import random from 'random-name' import Button from './button' import Item from './item' import './style.css'; export default class ReactTransitionGroup extends Component { constructor(props) { super(props); this.state = { items: ['Natividad Steen']}; this.handleAdd = this.handleAdd.bind(this); } handleAdd() { let newItems = this.state.items; newItems.push(random()); this.setState({ items: newItems }); } render () { const items = this.state.items.map((item, i) => ( <Item item={item} key={i} keyDelete={i} handleRemove={(i) => this.handleRemove(i)} /> )); return ( <Fragment> <Button onClick={this.handleAdd}/> <div className="project"> <CSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300} > {items} </CSSTransitionGroup> </div> </Fragment> ); } }; 

反应动画


React-animations是一个基于animate.css构建的库。 她的工作很轻松,她拥有许多不同的动画作品集。 该库与支持使用对象定义动画主要帧的任何内联样式库兼容,这些对象包括Radium,Aphrodite或样式组件。



我知道你的想法:



现在,让我们看一下它如何与弹跳动画示例结合使用。



首先,从反应动画导入动画。

const Bounce = styled.div`animation:2s $ {keyframes` $ {bounce}`} infinite`;

然后,在创建组件之后,我们将包装任何HTML代码或组件以进行动画处理。

 <bounce><h1>Hello Animation Bounce</h1></bounce> 

一个例子:

 import React, { Component } from 'react'; import styled, { keyframes } from 'styled-components'; import { bounce } from 'react-animations'; import './style.css'; const Bounce = styled.div`animation: 2s ${keyframes`${bounce}`} infinite`; export default class ReactAnimations extends Component { render() { return ( <Bounce><h1>Hello Animation Bounce</h1></bounce> ); } } 

一切正常,动画非常简单。 另外,在滚动-react-animate-on-scroll时,使用弹跳动画有一个很好的解决方案。

反应揭示


React Reveal框架具有基本的动画,包括淡入淡出,反射,缩放,旋转等。 这使得使用道具处理所有动画成为可能。 因此,您可以指定其他设置,包括位置,延迟,距离,级联等。 可以使用其他CSS效果,包括服务器端渲染和高阶组件。 通常,如果需要滚动动画,则应使用此框架。

 import Fade from 'react-reveal/Fade'; <Fade top> <h1>Title</h1> </Fade> 



总共有五个块,每个块都有一个全屏页面和一个标题。

 import React, { Component, Fragment } from 'react'; import Fade from 'react-reveal/Fade'; const animateList = [1, 2, 3, 4, 5]; export default class ReactReveal extends Component { render() { return ( <Fragment> {animateList.map((item, key) => ( <div style={styles.block} key={key}> <Fade top> <h1 style={styles.title}>{`block ${item}`}</h1> </Fade> </div> ))} </Fragment> ); } } const styles = { block: { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%', background: '#000', borderBottom: '1px solid rgba(255,255,255,.2)', }, title: { textAlign: 'center', fontSize: 100, color: '#fff', fontFamily: 'Lato, sans-serif', fontWeight: 100, }, }; 

现在我们介绍常量animateList。 该数组包含五个元素。 使用映射数组方法后,可以通过将元素插入标头来渲染Fade组件中的任何元素。 在styles常量中定义的样式会同时为块和标题获得简短的CSS样式。 上面是带有淡入淡出动画的五个块。

TweenOne和Ant Design中的动画


Ant Design是一个React UI库,其中包含大量有用且易于使用的组件。 如果您需要创建优雅的用户界面,则非常适合。 由阿里巴巴开发,该库在其许多项目中都使用该库。



该示例包含许多动画组件。 它们中的大多数具有相似的动画,因此示例实现将比上述示例更简单。 这将仅包括一个球,一个绿色球和一个附加元素,例如红色方块。



动画使用TweenOne组件,该组件需要PathPlugin才能正确设置轨迹。 所有这一切只有在放置时才有效
TweenOne.plugins上的PathPlugin。

 TweenOne.plugins.push(PathPlugin); 

主要的动画选项如下:

  • 持续时间-动画时间,以毫秒为单位;
  • 缓解-动画的平滑度;
  • yoyo-每次重复向前和向后移动的变化;
  • 重复-重复动画。 您需要使用-1进行无尽的动画处理;
  • p-动画路径的坐标;
  • easePath-动画的平滑路径的坐标。

最后两个参数非常具体,但是不用担心,一切都会正常进行。

 const duration = 7000; const ease = 'easeInOutSine'; const p = 'M123.5,89.5 C148,82.5 239.5,48.5 230,17.5 C220.5,-13.5 127,6 99.5,13.5 C72,21 -9.5,56.5 1.5,84.5 C12.5,112.5 99,96.5 123.5,89.5 Z'; const easePath = 'M0,100 C7.33333333,89 14.3333333,81.6666667 21,78 C25.3601456,75.6019199 29.8706084,72.9026327 33,70 C37.0478723,66.2454406 39.3980801,62.0758689 42.5,57 C48,46.5 61.5,32.5 70,28 C77.5,23.5 81.5,20 86.5,16 C89.8333333,13.3333333 94.3333333,8 100,0'; const loop = { yoyo: true, repeat: -1, duration, ease, }; 

现在,您可以开始创建动画对象。

  • redSquare包含循环参数以及Y坐标,持续时间和延迟。
  • greenBall包含带有对象x,y-p值的参数的路径。 此外,持续时间,重复性和平滑度是TweenOne.easing.path的函数,它具有两个参数。
  • 路径-easePath。
  • lengthPixel是仅分为400个部分的曲线。
  • track-带有轴的椭圆形,具有循环样式和旋转参数。

 const animate = { redSquare: { ...loop, y: 15, duration: 3000, delay: 200, }, greenBall: { path: { x: p, y: p }, duration: 5000, repeat: -1, ease: TweenOne.easing.path(easePath, { lengthPixel: 400 }), }, track: { ...loop, rotate: 15, }, }; 

您还需要注意TweenOne组件。 所有组件将从rc-tween-one导入。 TweenOne是具有基本propr和动画道具(即动画)的基本组件。 每个TweenOne都有自己的动画参数,例如redSquare,track,greenBall。

 import React from 'react'; import TweenOne from 'rc-tween-one'; export default function BannerImage() { return ( <div className="wrapper-ant-design"> <svg width="482px" height="500px" viewBox="0 0 482 500"> <defs> <path d="M151,55 C129.666667,62.6666667 116,74.3333333 110,90 C104,105.666667 103,118.5 107,128.5 L225.5,96 C219.833333,79 209.666667,67 195,60 C180.333333,53 165.666667,51.3333333 151,55 L137,0 L306.5,6.5 L306.5,156 L227,187.5 L61.5,191 C4.5,175 -12.6666667,147.833333 10,109.5 C32.6666667,71.1666667 75,34.6666667 137,0 L151,55 Z" id="mask" /> </defs> <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd" transform="translate(0, 30)"> <g id="Group-13" transform="translate(0.000000, 41.000000)"> <TweenOne component="g" animation={animate.redSquare}> <rect stroke="#F5222D" strokeWidth="1.6" transform="translate(184.000000, 18.000000) rotate(8.000000) translate(-184.000000, -18.000000) " x="176.8" y="150.8" width="14.4" height="14.4" rx="3.6" /> </TweenOne> </g> <g id="Group-14" transform="translate(150.000000, 230.000000)"> <g id="Group-22" transform="translate(62.000000, 7.000000)"> <image id="cc4" alt="globe" xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/FpKOqFadwoFFIZFExjaf.png" width="151px" height="234px" /> </g> <mask id="mask-2"> <use xlinkHref="#mask" fill="white" transform="translate(-42, -33)" /> </mask> <g mask="url(#mask-2)"> <TweenOne component="g" animation={animate.track} style={{ transformOrigin: '122.7px 58px' }}> <g transform="translate(-16, -52)"> <g transform="translate(16, 52)"> <path d="M83.1700911,35.9320015 C63.5256194,37.9279025 44.419492,43.1766434 25.8517088,51.6782243 C14.3939956,57.7126276 7.77167019,64.8449292 7.77167019,72.4866248 C7.77167019,94.1920145 61.1993389,111.787709 127.105708,111.787709 C193.012078,111.787709 246.439746,94.1920145 246.439746,72.4866248 C246.439746,55.2822262 212.872939,40.6598106 166.13127,35.3351955" id="line-s" stroke="#0D1A26" strokeWidth="1.35" strokeLinecap="round" transform="translate(127.105708, 73.561453) rotate(-16.000000) translate(-127.105708, -73.561453) " /> </g> <TweenOne component="g" animation={animate.greenBall}> <image alt="globe" id="id2" xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/IauKICnGjGnotJBEyCRK.png" x="16" y="62" width="26px" height="26px" /> </TweenOne> </g> </TweenOne> </g> </g> </g> </svg> </div> ); } 7.77167019,64.8449292 7.77167019,72.4866248 C7.77167019,94.1920145 61.1993389,111.787709 127.105708,111.787709 C193.012078,111.787709 246.439746,94.1920145 246.439746 import React from 'react'; import TweenOne from 'rc-tween-one'; export default function BannerImage() { return ( <div className="wrapper-ant-design"> <svg width="482px" height="500px" viewBox="0 0 482 500"> <defs> <path d="M151,55 C129.666667,62.6666667 116,74.3333333 110,90 C104,105.666667 103,118.5 107,128.5 L225.5,96 C219.833333,79 209.666667,67 195,60 C180.333333,53 165.666667,51.3333333 151,55 L137,0 L306.5,6.5 L306.5,156 L227,187.5 L61.5,191 C4.5,175 -12.6666667,147.833333 10,109.5 C32.6666667,71.1666667 75,34.6666667 137,0 L151,55 Z" id="mask" /> </defs> <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd" transform="translate(0, 30)"> <g id="Group-13" transform="translate(0.000000, 41.000000)"> <TweenOne component="g" animation={animate.redSquare}> <rect stroke="#F5222D" strokeWidth="1.6" transform="translate(184.000000, 18.000000) rotate(8.000000) translate(-184.000000, -18.000000) " x="176.8" y="150.8" width="14.4" height="14.4" rx="3.6" /> </TweenOne> </g> <g id="Group-14" transform="translate(150.000000, 230.000000)"> <g id="Group-22" transform="translate(62.000000, 7.000000)"> <image id="cc4" alt="globe" xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/FpKOqFadwoFFIZFExjaf.png" width="151px" height="234px" /> </g> <mask id="mask-2"> <use xlinkHref="#mask" fill="white" transform="translate(-42, -33)" /> </mask> <g mask="url(#mask-2)"> <TweenOne component="g" animation={animate.track} style={{ transformOrigin: '122.7px 58px' }}> <g transform="translate(-16, -52)"> <g transform="translate(16, 52)"> <path d="M83.1700911,35.9320015 C63.5256194,37.9279025 44.419492,43.1766434 25.8517088,51.6782243 C14.3939956,57.7126276 7.77167019,64.8449292 7.77167019,72.4866248 C7.77167019,94.1920145 61.1993389,111.787709 127.105708,111.787709 C193.012078,111.787709 246.439746,94.1920145 246.439746,72.4866248 C246.439746,55.2822262 212.872939,40.6598106 166.13127,35.3351955" id="line-s" stroke="#0D1A26" strokeWidth="1.35" strokeLinecap="round" transform="translate(127.105708, 73.561453) rotate(-16.000000) translate(-127.105708, -73.561453) " /> </g> <TweenOne component="g" animation={animate.greenBall}> <image alt="globe" id="id2" xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/IauKICnGjGnotJBEyCRK.png" x="16" y="62" width="26px" height="26px" /> </TweenOne> </g> </TweenOne> </g> </g> </g> </svg> </div> ); } 



是的,看起来很恐怖,但是使用此方法的动画很简单。

  <TweenOne component="g" animation={animate.redSquare} /> <TweenOne component="g" animation={animate.track} /> <TweenOne component="g" animation={animate.greenBall} /> 

您所需要做的就是描述动画规则,并将其转移到TweenOne组件。

为了实现不同的目标,需要不同的方法。 本文研究了可以在大量项目中使用的几种解决方案。 您的业​​务是选择合适的。
Skillbox建议:

Source: https://habr.com/ru/post/zh-CN456972/


All Articles