React怎么实现一个Transition过渡动画组件
这篇文章将为大家详细讲解有关React怎么实现一个Transition过渡动画组件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
一、基本实现
我们在实现基础的过度动画组件,需要通过切换CSS样式实现简单的动画效果。
首先我们安装 classnames 插件:
npminstallclassnames--save-dev
而且 classnaems 是一个简单的JavaScript实用程序,用于有条件地将 classnames
连接一起。那么我们将 component
目录新建一个 Transition
文件夹,并在文件夹中新建一个 Transition.jsx
文件,代码如下:
importReactfrom'react'importclassnamesfrom'classnames'/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{render(){const{children}=this.propsconsttransition=(<divclassName={classnames({transition:true})}style={{position:'relative',overflow:'hidden'}}><divclassName={classnames({'transition-wrapper':true})}>{children}</div></div>)returntransition}}exportdefaultTransition
在文件中我们通过使用小驼峰来定义属性定义名称;而且我们在文件中使用 JSX 语法,我们来看看如下案例代码:
constname='JoshPerez';constelement=<h2>Hello,{name}</h2>;
这个代码等价如下这串代码:
constelement=<h2>Hello,JoshPerez</h2>;
当然在使用 JSX 语法的时候我们还是要注意的,因为在 JSX 语法中会更接近 JavaScript
而不是 html
,所以在 React DOM 中我们使用 小驼峰来进行命名,而不使用 html
属性名称约定。
除此之外在 React 中的 props.children
包含组件所有的子节点,即组件开始标签和结束标签之间的内容,如下案例所示:
<Button>默认按钮</Button>
在 Button
组件中获取 props.children
,就可以得到字符串“默认按钮”。
那么接下来我们在 Transition 文件夹下新建一个 index.js
,导出 Transition
组件,代码如下所示:
importTransitionfrom'./Transition.jsx'export{Transition}exportdefaultTransition
完成之后,在 Transition.jsx
文件中为组件添加 props
检查并设置 action
默认值,代码如下所示:
importPropTypesfrom'prop-types'constpropTypes={/**执行动画*/action:PropTypes.bool,/**切换的css动画的class名称*/toggleClass:PropTypes.string}constdefaultProps={action:false}
这时候我们使用 prop-ty.pes实现运行时类型检查。但是需要注意的是 prop-ty.pes是一个运行时;类型检查工具,我们来看看完整的 Transition 组件代码如下所示:
importReactfrom'react'importPropTypesfrom'prop-types'importclassnamesfrom'classnames'constpropTypes={/**执行动画*/action:PropTypes.bool,/**切换的css动画的class名称*/toggleClass:PropTypes.string}constdefaultProps={action:false}/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{staticpropTypes=propTypesstaticdefaultProps=defaultPropsrender(){const{className,action,toggleClass,children}=this.propsconsttransition=(<divclassName={classnames({transition:true})}style={{position:'relative',overflow:'hidden'}}><divclassName={classnames({'transition-wrapper':true,[className]:className,[toggleClass]:action&&toggleClass})}>{children}</div></div>)returntransition}}exportdefaultTransition
CSS代码如下所示:
.fade{transition:opacity0.15slinear;}.fade:not(.show){opacity:0;}
JS代码如下所示:
importReactfrom'react';importTransitionfrom'./Transition';classAnimeextendsReact.Component{constructor(props){super(props)this.state={action:true}}render(){constbtnText=this.state.action?'淡出':'淡入'return(<div><TransitionclassName="fade"toggleClass="show"action={this.state.action}>淡入淡出</Transition><buttonstyle={{marginTop:'20px'}}onClick={()=>this.setState({action:!this.state.action})}>{btnText}</button></div>)}}
这样子我们就只需要在需要使用动画的地方来进行使用 Anime
组件就可以了。
二、实现Animate.css兼容
我们都知道 Animate.css
是一款强大的预设 CSS3 动画库。由于在进入动画和离开动画通常在使用这两个效果相反的 class
样式,所以我们需要给我们的 Transition
组件添加 enterClass
和 leaveClass
两个属性来实现动画的切换,代码如下所示:
importReactfrom'react'importPropTypesfrom'prop-types'importclassnamesfrom'classnames'constpropTypes={/**执行动画*/action:PropTypes.bool,/**切换的css动画的class名称*/toggleClass:PropTypes.string,/**进入动画的class名称,存在toggleClass时无效*/enterClass:PropTypes.string,/**离开动画的class名称,存在toggleClass时无效*/leaveClass:PropTypes.string}constdefaultProps={action:false}/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{staticpropTypes=propTypesstaticdefaultProps=defaultPropsrender(){const{className,action,toggleClass,enterClass,leaveClass,children}=this.propsreturn(<divclassName={classnames({transition:true})}style={{position:'relative',overflow:'hidden'}}><divclassName={classnames({'transition-wrapper':true,[className]:className,[toggleClass]:action&&toggleClass,[enterClass]:!toggleClass&&action&&enterClass,[leaveClass]:!toggleClass&&!action&&leaveClass,})}>{children}</div></div>)}}exportdefaultTransition
当然我们还是要注意一下,由于 toggleClass
适用于那些进入动画与离开动画切换相同 class
样式的情况,而且 enterClass
和 leaveClass
使用那些进入动画和离开动画切换不同的 class
样式的情况,所以,他们和 toggleClass
不能共存。
那么我们接下来就尝试下加入Animate.css
后的 Transition
组件,代码如下所示:
importReactfrom'react';import'animate.css';classAnimeextendsReact.Component{constructor(props){super(props)this.state={action:true}}render(){return(<div><TransitionclassName="animated"enterClass="bounceInLeft"leaveClass="bounceOutLeft"action={this.state.action}>弹入弹出</Transition><uttonstyle={{marginTop:'20px'}}onClick={()=>this.setState({action:!this.state.action})}>{this.state.action?'弹出':'弹入'}</utton></div>)}}
三、功能扩展
通过上面的方法实现之后我们知道 Transition
组件是可以适用在很多的场景中的,但是功能不是很丰富,所以就需要扩展 Transition
的接口。首先我们来添加 props
属性,并设置默认值,代码如下所示:
constpropTypes={...,/**动画延迟执行时间*/delay:PropTypes.string,/**动画执行时间长度*/duration:PropTypes.string,/**动画执行次数,只在执行CSS3动画时有效*/count:PropTypes.number,/**动画缓动函数*/easing:PropTypes.oneOf(['linear','ease','ease-in','ease-out','ease-in-out']),/**是否强制轮流反向播放动画,count为1时无效*/reverse:PropTypes.bool}constdefaultProps={count:1,reverse:false}
根据 props 设置样式,代码如下所示:
//动画样式conststyleText=(()=>{letstyle={}//设置延迟时长if(delay){style.transitionDelay=delaystyle.animationDelay=delay}//设置播放时长if(duration){style.transitionDuration=durationstyle.animationDuration=duration}//设置播放次数if(count){style.animationIterationCount=count}//设置缓动函数if(easing){style.transitionTimingFunction=easingstyle.animationTimingFunction=easing}//设置动画方向if(reverse){style.animationDirection='alternate'}returnstyle})()
完整代码如下所示:
importReactfrom'react'importPropTypesfrom'prop-types'importclassnamesfrom'classnames'constpropTypes={/**执行动画*/action:PropTypes.bool,/**切换的css动画的class名称*/toggleClass:PropTypes.string,/**进入动画的class名称,存在toggleClass时无效*/enterClass:PropTypes.string,/**离开动画的class名称,存在toggleClass时无效*/leaveClass:PropTypes.string,/**动画延迟执行时间*/delay:PropTypes.string,/**动画执行时间长度*/duration:PropTypes.string,/**动画执行次数,只在执行CSS3动画时有效*/count:PropTypes.number,/**动画缓动函数*/easing:PropTypes.oneOf(['linear','ease','ease-in','ease-out','ease-in-out']),/**是否强制轮流反向播放动画,count为1时无效*/reverse:PropTypes.bool}constdefaultProps={action:false,count:1,reverse:false}/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{staticpropTypes=propTypesstaticdefaultProps=defaultPropsrender(){const{className,action,toggleClass,enterClass,leaveClass,delay,duration,count,easing,reverse,children}=this.props//动画样式conststyleText=(()=>{letstyle={}//设置延迟时长if(delay){style.transitionDelay=delaystyle.animationDelay=delay}//设置播放时长if(duration){style.transitionDuration=durationstyle.animationDuration=duration}//设置播放次数if(count){style.animationIterationCount=count}//设置缓动函数if(easing){style.transitionTimingFunction=easingstyle.animationTimingFunction=easing}//设置动画方向if(reverse){style.animationDirection='alternate'}returnstyle})()return(<divclassName={classnames({transition:true})}style={{position:'relative',overflow:'hidden'}}><divclassName={classnames({'transition-wrapper':true,[className]:className,[toggleClass]:action&&toggleClass,[enterClass]:!toggleClass&&action&&enterClass,[leaveClass]:!toggleClass&&!action&&leaveClass,})}style={styleText}>{children}</div></div>)}}exportdefaultTransition
在这里我们来看下相关的 Transition 增加的属性:
delay:规定在动画开始之前的延迟。
duration:规定完成动画所花费的时间,以秒或毫秒计。
count:规定动画应该播放的次数。
easing:规定动画的速度曲线。
reverse:规定是否应该轮流反向播放动画。
四、优化
那么接下来我们来对 Transition 来进行一个优化,我们主要的是动画监听、卸载组件以及其他的相关兼容问题。那么我们在代码中添加 props 属性,并且设置默认值。代码如下所示:
constpropTypes={...,/**动画结束的回调*/onEnd:PropTypes.func,/**离开动画结束时卸载元素*/exist:PropTypes.bool}constdefaultProps={...,reverse:false,exist:false}
接下来进行动画结束监听的事件代码:
/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{...onEnd=e=>{const{onEnd,action,exist}=this.propsif(onEnd){onEnd(e)}//卸载DOM元素if(!action&&exist){constnode=e.target.parentNodenode.parentNode.removeChild(node)}}/***对动画结束事件onEnd回调的处理函数**@param{string}type-事件解绑定类型:add-绑定事件,remove-移除事件绑定*/handleEndListener(type='add'){constel=ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')constevents=['animationend','transitionend']events.forEach(ev=>{el[`${type}EventListener`](ev,this.onEnd,false)})}componentDidMount(){this.handleEndListener()}componentWillUnmount(){const{action,exist}=this.propsif(!action&&exist){this.handleEndListener('remove')}}render(){...}}
在代码中我们可以知道使用到 componentDidMount 和 componentWillUnmount 这两个生命周期函数。
react-dom 中还为我们提供了可以在 React 应用中使用的 DOM 方法。我们通过获取兼容性 animationend 和transitionend 事件。检验函数方法的代码如下所示:
/***浏览器兼容事件检测函数**@param{node}el-触发事件的DOM元素*@param{array}events-可能的事件类型*@returns{*}*/constwhichEvent=(el,events)=>{constlen=events.lengthfor(vari=0;i<len;i++){if(el.style[i]){returnevents[i];}}}
修改 handleEndListener 函数代码如下所示:
/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{.../***对动画结束事件onEnd回调的处理函数**@param{string}type-事件解绑定类型:add-绑定事件,remove-移除事件绑定*/handleEndListener(type='add'){constel=ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')constevents=['AnimationEnd','TransitionEnd']events.forEach(ev=>{consteventType=whichEvent(el,[ev.toLowerCase(),`webkit${ev}`])el[`${type}EventListener`](eventType,this.onEnd,false)})}...}
那么到这里之后我们就完成了整个 Transition 组件的开发,相关完整代码如下所示:
importReactfrom'react'importPropTypesfrom'prop-types'importclassnamesfrom'classnames'importReactDOMfrom'react-dom'constpropTypes={/**执行动画*/action:PropTypes.bool,/**切换的css动画的class名称*/toggleClass:PropTypes.string,/**进入动画的class名称,存在toggleClass时无效*/enterClass:PropTypes.string,/**离开动画的class名称,存在toggleClass时无效*/leaveClass:PropTypes.string,/**动画延迟执行时间*/delay:PropTypes.string,/**动画执行时间长度*/duration:PropTypes.string,/**动画执行次数,只在执行CSS3动画时有效*/count:PropTypes.number,/**动画缓动函数*/easing:PropTypes.oneOf(['linear','ease','ease-in','ease-out','ease-in-out']),/**是否强制轮流反向播放动画,count为1时无效*/reverse:PropTypes.bool,/**动画结束的回调*/onEnd:PropTypes.func,/**离开动画结束时卸载元素*/exist:PropTypes.bool}constdefaultProps={action:false,count:1,reverse:false,exist:false}/***浏览器兼容事件检测函数**@param{node}el-触发事件的DOM元素*@param{array}events-可能的事件类型*@returns{*}*/constwhichEvent=(el,events)=>{constlen=events.lengthfor(vari=0;i<len;i++){if(el.style[i]){returnevents[i];}}}/***css过渡动画组件**@visibleNameTransition过渡动画*/classTransitionextendsReact.Component{staticpropTypes=propTypesstaticdefaultProps=defaultPropsonEnd=e=>{const{onEnd,action,exist}=this.propsif(onEnd){onEnd(e)}//卸载DOM元素if(!action&&exist){constnode=e.target.parentNodenode.parentNode.removeChild(node)}}/***对动画结束事件onEnd回调的处理函数**@param{string}type-事件解绑定类型:add-绑定事件,remove-移除事件绑定*/handleEndListener(type='add'){constel=ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')constevents=['AnimationEnd','TransitionEnd']events.forEach(ev=>{consteventType=whichEvent(el,[ev.toLowerCase(),`webkit${ev}`])el[`${type}EventListener`](eventType,this.onEnd,false)})}componentDidMount(){this.handleEndListener()}componentWillUnmount(){const{action,exist}=this.propsif(!action&&exist){this.handleEndListener('remove')}}render(){const{className,action,toggleClass,enterClass,leaveClass,delay,duration,count,easing,reverse,children}=this.props//动画样式conststyleText=(()=>{letstyle={}//设置延迟时长if(delay){style.transitionDelay=delaystyle.animationDelay=delay}//设置播放时长if(duration){style.transitionDuration=durationstyle.animationDuration=duration}//设置播放次数if(count){style.animationIterationCount=count}//设置缓动函数if(easing){style.transitionTimingFunction=easingstyle.animationTimingFunction=easing}//设置动画方向if(reverse){style.animationDirection='alternate'}returnstyle})()consttransition=(<divclassName={classnames({transition:true})}style={{position:'relative',overflow:'hidden'}}><divclassName={classnames({'transition-wrapper':true,[className]:className,[toggleClass]:action&&toggleClass,[enterClass]:!toggleClass&&action&&enterClass,[leaveClass]:!toggleClass&&!action&&leaveClass,})}style={styleText}>{children}</div></div>)returntransition}}exportdefaultTransition
关于“React怎么实现一个Transition过渡动画组件”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。