There are several common animation implementations in React, and several react animation implementations
At present, users' requirements for front-end pages cannot meet the needs of functions, but they must be well-known and interesting. In addition to the overall UI appearance, adding appropriate animation effects in the right place is often more expressive than static pages to achieve more natural effects. For example, a simple loading animation or page switching effect can not only ease users' waiting mood, but also achieve brand promotion through brand logo and other forms.
As a Popular Front-end development framework in recent years, React puts forward the concept of virtual DOM. All DOM changes first occur on the virtual DOM and analyze the actual changes of web pages through DOM diff, then it is reflected in the real DOM, which greatly improves the web page performance. However, in terms of animation implementation, React, as a framework, does not directly provide animation effects to components and must be implemented by developers themselves. Most traditional web animations are implemented by directly operating actual DOM elements, this is obviously not promoted in React. So how can animation be implemented in React?
The essence of all animations is to continuously modify one or more attributes of DOM elements to produce a consistent effect of changes, thus forming an animation. In React, animation is essentially the same as traditional web Animation. There are still two ways to achieve it through css3 animation and modify element attributes through js. In actual implementation, the React framework features must be better met, which can be summarized into several categories:
- Interval Animation Based on timer or requestAnimationFrame (RAF;
- Simple animations Based on css3;
- CssTransitionGroup;
- Implement complex animation with hook;
- Other third-party animation libraries.
1. interval Animation Based on timer or RAF
At the earliest, animation implementation relied on timers.setInterval
,setTimeout
OrrequestAnimationFrame
(RAF) directly modify the attributes of the DOM element. Developers who are not familiar with the React feature may habitually passref
OrfindDOMNode()
Get the real DOM node and directly modify its style. Howeverref
Directly obtaining the real DOM and performing operations on it is not recommended. Avoid such operations as far as possible.
Therefore, we need to pass the timer, RAF, and other methods with the DOM node attributestate
Contact. First, you need to extract the attributes related to the change style and replace themstate
And then add the timer orrequestAnimationFrame
Continuous Modificationstate
To trigger component updates to achieve the animation effect.
Example
Take a progress bar as an example. The Code is as follows:
// Use requestAnimationFrame to change stateimport React, {Component} from 'react '; export default class Progress extends Component {constructor (props) {super (props); 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);} decrease = () => {const percent = this. state. percent; const targetPercent = pe Rcent <10? 0: percent-10; const speed = (percent-targetPercent)/400; let start = null; const animate = timestamp => {if (! Start) start = timestamp; const progress = timestamp-start; const currentProgress = Math. max (parseInt (percent-speed * progress, 10), targetPercent); this. setState ({percent: currentProgress}); if (currentProgress> targetPercent) {window. requestAnimationFrame (animate) ;}}; window. requestAnimationFrame (animate);} render () {const {percent} = this. state; return (<div> <div className = "progress"> <div className = "progress-wrapper"> <div className = "progress-inner" style = {width: '$ {percent} %' }}> </div> <div className = "progress-info"> {percent }%</div> </div> <div className = "btns"> <button onClick = {this. decrease}>-</button> <button onClick = {this. increase >>+ </button> </div> );}}
In the exampleincrease
Anddecrease
Build a linear transition function in the functionanimation
,requestAnimationFrame
Execute the transition function before each re-painting in the browser to calculate the current progress bar.width
Attribute and updatestate
To re-render the progress bar. The effect of this example is as follows:
This implementation method is in userequestAnimationFrame
When the performance is good, it is completely implemented using pure js and does not rely on css. Frame freezing may occur when the timer is used. In addition, developers also need to calculate their own states based on the speed function, which is complicated.
2. Simple animations Based on css3
In css3animation
Andtransition
After appearance and popularization, we can easily use css to change element styles without manually calculating real-time styles.
Example
The above progress bar is used as an example to implement the dynamic progress bar using css3. The Code is as follows:
Import React, {Component} from 'react '; export default class Progress extends Component {constructor (props) {super (props); this. state = {percent: 10 };} increase = () => {const percent = this. state. percent + 10; this. setState ({percent: percent> 100? 100: percent,})} decrease = () => {const percent = this. state. percent-10; this. setState ({percent: percent <0? 0: percent,})} render () {// same as above, omitted ....}}
. Progress-inner {transition: width 400 ms cubic-bezr (0.08, 0.82, 0.17, 1); // other styles are the same as above, omitted ...}
In the example,increase
Anddecrease
Function is no longer computedwidth
, But directly set the width after the increase or decrease. It should be noted thattransition
Attribute in the specifiedtransition-property
When a style changes, the style changes dynamically and the speed curves of different speed effects can be set. The results of this example are shown in. Different from the previous example, the Progress data on the right side is directly changed to the target number without a specific change process, the dynamic effect of the progress bar is no longer linear, and the effect is more vivid.
The Implementation Method Based on css3 has a high performance and a small amount of code, but it only depends on the css effect and is difficult to implement complicated animations. In addition, by modifyingstate
The animation effect can only be applied to nodes that already exist in the DOM tree. If you want to add an entry and exit animation for the component in this way, you must maintain at least twostate
To achieve entry and exit animation, one of whichstate
Used to control whether the element is displayed.state
Controls the changing attributes of an element in an animation. In this case, developers need to spend a lot of effort to maintain the animation logic of components, which is complex and cumbersome.
3. CssTransitionGroup, React animation plug-in
React has provided animation plug-ins for developers.react-addons-css-transition-group
And then handed over to the community for maintenance to form the currentreact-transition-group
This plug-in allows you to easily implement the entry and exit animations of components. You need to install this plug-in an additional way.react-transition-group
IncludeCSSTransitionGroup
AndTransitionGroup
The latter is the underlying api. The former is further encapsulated by the latter, making css animation easier.
Example
The code for dynamically adding a tab is as follows:
Import React, {Component} from 'react '; import {CSSTransitionGroup} from 'react-transition-group'; let uid = 2; export default class Tabs extends Component {constructor (props) {super (props); this. state = {activeId: 1, tabData: [{id: 1, panel: 'option 1'}, {id: 2, panel: 'option 2'}]};} addTab = () => {// Add the tab code ...} deleteTab = (id) = >{// Delete the tab code ...} render () {const {tabData, activeId} = This. state; const renderTabs = () => {return tabData. map (item, index) =>{ return (<div className = {'tab-item $ {item. id === activeId? 'Tab-item-activity': ''} '} key = {'tab $ {item. id} '}> {item. panel} <span className = "btns btn-delete" onClick = {() => this. deleteTab (item. id) }> cursor </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> );}}
/* Dynamically add an animation to the tab */. tabs-wrap-enter {opacity: 0.01 ;}. tabs-wrap-enter.tabs-wrap-enter-active {opacity: 1; transition: all 500 ms milliseconds-in ;}. tabs-wrap-leave {opacity: 1 ;}. tabs-wrap-leave.tabs-wrap-leave-active {opacity: 0.01; transition: all 500 ms latency-in ;}
CSSTransitionGroup
You can add additional css classes for its subnodes, and then use css animations to achieve entry and exit animations. To add animation effects to each tab node, you must first wrap them inCSSTransitionGroup
Component. When settransitionName
The property is'tabs-wrapper'
,transitionEnterTimeout
After 400 milliseconds, onceCSSTransitionGroup
The newly added node will be added to the css class when it appears.'tabs-wrapper-enter'
And then added the css class at the next frame.'tabs-wrapper-enter-active'
. Because these two css classes have different transparency and css3 transition attributes, the node achieves the Entry Effect of transparency from small to large. 400 milliseconds later css class'tabs-wrapper-enter'
And'tabs-wrapper-enter-active'
Will be removed at the same time, and the node will complete the whole admission animation process. The implementation of the exit animation is similar to the entrance animation, except that the added css class is named'tabs-wrapper-leave'
And'tabs-wrapper-leave-active'
. Shows the effect of this example:
CSSTransitionGroup
The following seven attributes are supported:
The admission and departure animations are enabled by default and must be set during use.transitionEnterTimeout
AndtransitionLeaveTimeout
. It is worth noting that,CSSTransitionGroup
It also provides an animation (appear) that needs to be set during use.transitionAppearTimeout
. So what is the difference between an animation and an admission animation? When settransitionAppear
Istrue
,CSSTransitionGroup
InFirst RenderingA stage is added. In this phase,CSSTransitionGroup
Existing subnodes will be added to the css class one after another.'tabs-wrapper-appear'
And'tabs-wrapper-appear-active'
To achieve the animation effect. Therefore, an animation only appliesCSSTransitionGroup
A subnode exists during the first rendering.CSSTransitionGroup
After rendering is complete, its subnodes may only have an entry animation (enter) and an animation (appear ).
In additionCSSTransitionGroup
Note the following:
CSSTransitionGroup
By default,span
Label packages its subnodes. If you want to use other html tags, you can setCSSTransitionGroup
Ofcomponent
Attribute;
CSSTransitionGroup
Must be addedkey
When the node changes, the system accurately calculates which nodes need to be added with an entry animation and which nodes need to be added with an exit animation;
CSSTransitionGroup
The animation effect is only applied to direct subnodes, but not to their child nodes;
- The animation end time is not based on the transition-duration in css,
transitionEnterTimeout
,transitionLeaveTimeout
,TransitionAppearTimeout
In some cases, the transitionend event is not triggered. For details, see MDN transitionend.
CSSTransitionGroup
Advantages of Animation:
- It is easy to use and allows you to conveniently and quickly implement the entry and exit animations of elements;
- Combined with React, the performance is better.
CSSTransitionGroup
The disadvantages are also obvious:
- Limited to animation, admission animation, and departure animation;
- Due to the need to develop
transitionName
, Insufficient flexibility;
- You can only rely on css for simple animation.
4. Complex animation with hook
In actual projects, some cool animation effects may be required. These effects are only dependent on css3 and are often difficult to implement. At this time, we may wish to use some mature third-party libraries, such as jQuery or GASP, combined with the lifecycle hook function in the React component to achieve complex animation effects. Except for the normal life cycle of the React component,CSSTransitionGroup
Underlying apiTransitonGroup
It also provides a series of special lifecycle hook functions for its child elements. These hook functions can be combined with a third-party animation library to achieve a wide range of entrance and departure animation effects.
TransisitonGroup
The following six lifecycle hook functions are provided:
- ComponentWillAppear (callback)
- ComponentDidAppear ()
- ComponentWillEnter (callback)
- ComponentDidEnter ()
- ComponentWillLeave (callback)
- ComponentDidLeave ()
Their triggering time:
Example
GASP is an animation library developed so far in the flash era. It draws on the concept of Video Frames and is especially suitable for the Series animation effect for a long time. In this article, we useTransitonGroup
Andreact-gsap-enhancer
(A gsap can be applied to the React enhancement Library) to complete an image gallery. The Code is as follows:
import React, { Component } from 'react'; import { TransitionGroup } from 'react-transition-group'; import GSAP from 'react-gsap-enhancer' import { TimelineMax, Back, Sine } from 'gsap';class Photo extends Component { constructor(props) { super(props); } componentWillEnter(callback) { this.addAnimation(this.enterAnim, {callback: callback}) } componentWillLeave(callback) { this.addAnimation(this.leaveAnim, {callback: callback}) } enterAnim = (utils) => { const { id } = this.props; return new TimelineMax() .from(utils.target, 1, { x: `+=${( 4 - id ) * 60}px`, autoAlpha: 0, onComplete: utils.options.callback, }, id * 0.7); } leaveAnim = (utils) => { const { id } = this.props; return new TimelineMax() .to(utils.target, 0.5, { scale: 0, ease: Sine.easeOut, onComplete: utils.options.callback, }, (4 - id) * 0.7); } render() { const { url } = this.props; return ( <div className="photo"> </div> ) }}const WrappedPhoto = GSAP()(Photo);export default class Gallery extends Component { constructor(props) { super(props); this.state = { show: false, photos: [{ id: 1, url: 'http://img4.imgtn.bdimg.com/it/u=1032683424,3204785822&fm=214&gp=0.jpg' }, { id: 2, url: 'http://imgtu.5011.net/uploads/content/20170323/7488001490262119.jpg' }, { id: 3, url: 'http://tupian.enterdesk.com/2014/lxy/2014/12/03/18/10.jpg' }, { id: 4, url: 'http://img4.imgtn.bdimg.com/it/u=360498760,1598118672&fm=27&gp=0.jpg' }] }; } toggle = () => { this.setState({ show: !this.state.show }) } render() { const { show, photos } = this.state; const renderPhotos = () => { return photos.map((item, index) => { return <WrappedPhoto id={item.id} url={item.url} key={`photo${item.id}`} />; }) } return ( <div> <button onClick={this.toggle}>toggle</button> <TransitionGroup component="div"> {show && renderPhotos()} </TransitionGroup> </div> ); }}
In this examplePhoto
OfcomponentWillEnter
AndcomponentWillLeave
Two hook functions add an admission animation for each child componententerAnim
And departure AnimationLeaveAnim
. In the admission animation, useTimeLineMax.from(target, duration, vars, delay)
Method To create a timeline animation, specifying the animation movement distance of each sub-component alongid
Increase and decrease, and the delay time variesid
The extension time of each child component in the departure animation increasesid
Increase and decrease, so as to realizeid
Different animations have different effects. You can add different effects to any sub-component as needed. Shows the effect of this example:
In useTransitionGroup
IncomponentnWillAppear(callback)
,componentnWillEntercallback)
,componentnWillLeave(callback)
The function must be called after the function logic ends.callback
To guaranteeTransitionGroup
Correctly maintains the status sequence of sub-nodes.
Animation with hook can support a variety of complex animations, such as time series animations. Due to the dependency on third-party libraries, the animation effect is often smoother and the user experience is better. However, the introduction of third-party libraries requires developers to learn the corresponding APIs and improve Code complexity.
V. other third-party animation Libraries
There are also many excellent third-party animation libraries, such as react-motion, Animated, and velocity-react.
Animated
Animated is a cross-platform animation library that is compatible with React and React Native. In the animation process, we only care about the initial state, ending state, and changing functions of the animation, and do not care about the specific values of element attributes at each time point. Therefore, Animatied adopts a declarative animation, it provides a specific method for calculating css objects and passing inAnimated.div
Animation effect.
Example
We use Animated to implement the image flip effect. The Code is as follows.
Import React, {Component} from 'react '; import Animated from 'animated/lib/targets/react-dom'; export default class PhotoPreview extends Component {constructor (props) {super (props); this. state = {anim: new Animated. value (0) };} handleClick = () =>{ const {anim} = this. state; anim. stopAnimation (value => {Animated. spring (anim, {toValue: Math. round (value) + 1 }). start () ;}) ;}render () {const {anim} = this. state; const rotateDegree = anim. interpolate ({inputRange: [0, 4], outputRange: ['0deg ', '360deg']}); return (<div> <button onClick = {this. handleClick}> flip right </button> <Animated. div style = {transform: [{rotate: rotateDegree}]} className = "preivew-wrapper"> </Animated. div ></ div> );}}
In this example, we want to rotate the image 90 ° to the right after each button is clicked. A new value of 0 is created during component initialization.Animated
Objectthis.state.anim
In the render function, use the interpolation function.interpolate
AccordingAnimated
The current value of the object is calculated to obtain the corresponding rotation angle.rotateDegree
. Let's assume that every time you click a button,Animated
The value of the object is 1, and the corresponding image turns 90 °. Therefore, Setinterpolate
The input range of the function is [0, 4], and the output range is ['0deg ', '360deg'] for linear interpolation. IfAnimated
The current value of the object is 2, and the rotation angle is 180deg. In the component rendering structure, you need to useAnimated.div
Wrap the animation node and encapsulate the changed element attributes as css objects as stlye.Animated.div
. In the click event, we first usestopAnimation
Stop the current animation and obtainAnimated
Current Value of the objectvalue
And then useAnimated.spring
The function enables a spring animation process to achieve a smooth animation effect. Since each rotation stops, we want the image flip angle to be an integer multiple of 90 °, so we needAnimated.spring
End value. Finally, we achieved the following results:
Pay attention to the following points during use:
Animated
The value of an object and its interpolation result can only be appliedAnimated.div
Node;
interpolate
By default, linear interpolation is performed based on the input and output intervals. If the input value exceeds the input range, the interpolation result will be extended by default based on the output range. You can setextrapolate
The property limits the interpolation result range.
The animation does not directly modify the component.state
Instead of repeatedly triggering the render function, you can directly modify the attributes of elements through the components and methods of the newly created object. It is a very stable animation library in React Native. However, there is a low version browser compatibility problem in React, and there is a certain learning cost.
Conclusion
When we implement animation in React, we first need to consider the difficulty of the animation and use cases. For simple animation, we should first use css3 for implementation, followed by js-based time interval animation. If it is an element admission animation or an exit animation, we recommend that you combineCSSTransitionGroup
OrTransitionGroup
. When you want to achieve more complex animation effects, try some excellent third-party libraries to open the door to exciting animation effects.
Ps. All the sample code in this article can be viewed on github.
References:
React-transition-group
React-gsap-enhancer
A Comparison of Animation Technologies
React Animations in Depth
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.