您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關React怎么實現一個Transition過渡動畫組件,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
一、基本實現
我們在實現基礎的過度動畫組件,需要通過切換CSS樣式實現簡單的動畫效果。
首先我們安裝 classnames 插件:
npm install classnames --save-dev
而且 classnaems 是一個簡單的JavaScript實用程序,用于有條件地將 classnames
連接一起。那么我們將 component
目錄新建一個 Transition
文件夾,并在文件夾中新建一個 Transition.jsx
文件,代碼如下:
import React from 'react'
import classnames from 'classnames'
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
render() {
const { children } = this.props
const transition = (
<div
className={
classnames({
transition: true
})
}
style={
{
position: 'relative',
overflow: 'hidden'
}
}
>
<div
className={
classnames({
'transition-wrapper': true
})
}
>
{ children }
</div>
</div>
)
return transition
}
}
export default Transition
在文件中我們通過使用小駝峰來定義屬性定義名稱;而且我們在文件中使用 JSX 語法,我們來看看如下案例代碼:
const name = 'Josh Perez';
const element = <h2>Hello, {name}</h2>;
這個代碼等價如下這串代碼:
const element = <h2>Hello, Josh Perez</h2>;
當然在使用 JSX 語法的時候我們還是要注意的,因為在 JSX 語法中會更接近 JavaScript
而不是 html
,所以在 React DOM 中我們使用 小駝峰來進行命名,而不使用 html
屬性名稱約定。
除此之外在 React 中的 props.children
包含組件所有的子節點,即組件開始標簽和結束標簽之間的內容,如下案例所示:
<Button>默認按鈕</Button>
在 Button
組件中獲取 props.children
,就可以得到字符串“默認按鈕”。
那么接下來我們在 Transition 文件夾下新建一個 index.js
,導出 Transition
組件,代碼如下所示:
import Transition from './Transition.jsx'
export { Transition }
export default Transition
完成之后,在 Transition.jsx
文件中為組件添加 props
檢查并設置 action
默認值,代碼如下所示:
import PropTypes from 'prop-types'
const propTypes = {
/** 執行動畫 */
action: PropTypes.bool,
/** 切換的css動畫的class名稱 */
toggleClass: PropTypes.string
}
const defaultProps = {
action: false
}
這時候我們使用 prop-ty.pes實現運行時類型檢查。但是需要注意的是 prop-ty.pes是一個運行時;類型檢查工具,我們來看看完整的 Transition 組件代碼如下所示:
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
const propTypes = {
/** 執行動畫 */
action: PropTypes.bool,
/** 切換的css動畫的class名稱 */
toggleClass: PropTypes.string
}
const defaultProps = {
action: false
}
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
static propTypes = propTypes
static defaultProps = defaultProps
render() {
const {
className,
action,
toggleClass,
children
} = this.props
const transition = (
<div
className={
classnames({
transition: true
})
}
style={
{
position: 'relative',
overflow: 'hidden'
}
}
>
<div
className={
classnames({
'transition-wrapper': true,
[className]: className,
[toggleClass]: action && toggleClass
})
}
>
{ children }
</div>
</div>
)
return transition
}
}
export default Transition
CSS代碼如下所示:
.fade {
transition: opacity 0.15s linear;
}
.fade:not(.show) {
opacity: 0;
}
JS代碼如下所示:
import React from 'react';
import Transition from './Transition';
class Anime extends React.Component {
constructor (props) {
super(props)
this.state = {
action: true
}
}
render () {
const btnText = this.state.action ? '淡出' : '淡入'
return (
<div>
<Transition
className="fade"
toggleClass="show"
action={ this.state.action }
>
淡入淡出
</Transition>
<button
style={{ marginTop: '20px' }}
onClick={() => this.setState({ action: !this.state.action })}
>
{ btnText }
</button>
</div>
)
}
}
這樣子我們就只需要在需要使用動畫的地方來進行使用 Anime
組件就可以了。
二、實現Animate.css兼容
我們都知道 Animate.css
是一款強大的預設 CSS3 動畫庫。由于在進入動畫和離開動畫通常在使用這兩個效果相反的 class
樣式,所以我們需要給我們的 Transition
組件添加 enterClass
和 leaveClass
兩個屬性來實現動畫的切換,代碼如下所示:
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
const propTypes = {
/** 執行動畫 */
action: PropTypes.bool,
/** 切換的css動畫的class名稱 */
toggleClass: PropTypes.string,
/** 進入動畫的class名稱,存在 toggleClass 時無效 */
enterClass: PropTypes.string,
/** 離開動畫的class名稱,存在 toggleClass 時無效 */
leaveClass: PropTypes.string
}
const defaultProps = {
action: false
}
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
static propTypes = propTypes
static defaultProps = defaultProps
render() {
const {
className,
action,
toggleClass,
enterClass,
leaveClass,
children
} = this.props
return (
<div
className={
classnames({
transition: true
})
}
style={
{
position: 'relative',
overflow: 'hidden'
}
}
>
<div
className={
classnames({
'transition-wrapper': true,
[className]: className,
[toggleClass]: action && toggleClass,
[enterClass]: !toggleClass && action && enterClass,
[leaveClass]: !toggleClass && !action && leaveClass,
})
}
>
{ children }
</div>
</div>
)
}
}
export default Transition
當然我們還是要注意一下,由于 toggleClass
適用于那些進入動畫與離開動畫切換相同 class
樣式的情況,而且 enterClass
和 leaveClass
使用那些進入動畫和離開動畫切換不同的 class
樣式的情況,所以,他們和 toggleClass
不能共存。
那么我們接下來就嘗試下加入Animate.css
后的 Transition
組件,代碼如下所示:
import React from 'react';
import 'animate.css';
class Anime extends React.Component {
constructor (props) {
super(props)
this.state = {
action: true
}
}
render () {
return (
<div>
<Transition
className="animated"
enterClass="bounceInLeft"
leaveClass="bounceOutLeft"
action={ this.state.action }
>
彈入彈出
</Transition>
<utton
style={{ marginTop: '20px' }}
onClick={() => this.setState({ action: !this.state.action })}
>
{ this.state.action ? '彈出' : '彈入' }
</utton>
</div>
)
}
}
三、功能擴展
通過上面的方法實現之后我們知道 Transition
組件是可以適用在很多的場景中的,但是功能不是很豐富,所以就需要擴展 Transition
的接口。首先我們來添加 props
屬性,并設置默認值,代碼如下所示:
const propTypes = {
...,
/** 動畫延遲執行時間 */
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
}
const defaultProps = {
count: 1,
reverse: false
}
根據 props 設置樣式,代碼如下所示:
// 動畫樣式
const styleText = (() => {
let style = {}
// 設置延遲時長
if (delay) {
style.transitionDelay = delay
style.animationDelay = delay
}
// 設置播放時長
if (duration) {
style.transitionDuration = duration
style.animationDuration = duration
}
// 設置播放次數
if (count) {
style.animationIterationCount = count
}
// 設置緩動函數
if (easing) {
style.transitionTimingFunction = easing
style.animationTimingFunction = easing
}
// 設置動畫方向
if (reverse) {
style.animationDirection = 'alternate'
}
return style
})()
完整代碼如下所示:
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
const propTypes = {
/** 執行動畫 */
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
}
const defaultProps = {
action: false,
count: 1,
reverse: false
}
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
static propTypes = propTypes
static defaultProps = defaultProps
render() {
const {
className,
action,
toggleClass,
enterClass,
leaveClass,
delay,
duration,
count,
easing,
reverse,
children
} = this.props
// 動畫樣式
const styleText = (() => {
let style = {}
// 設置延遲時長
if (delay) {
style.transitionDelay = delay
style.animationDelay = delay
}
// 設置播放時長
if (duration) {
style.transitionDuration = duration
style.animationDuration = duration
}
// 設置播放次數
if (count) {
style.animationIterationCount = count
}
// 設置緩動函數
if (easing) {
style.transitionTimingFunction = easing
style.animationTimingFunction = easing
}
// 設置動畫方向
if (reverse) {
style.animationDirection = 'alternate'
}
return style
})()
return (
<div
className={
classnames({
transition: true
})
}
style={
{
position: 'relative',
overflow: 'hidden'
}
}
>
<div
className={
classnames({
'transition-wrapper': true,
[className]: className,
[toggleClass]: action && toggleClass,
[enterClass]: !toggleClass && action && enterClass,
[leaveClass]: !toggleClass && !action && leaveClass,
})
}
style={ styleText }
>
{ children }
</div>
</div>
)
}
}
export default Transition
在這里我們來看下相關的 Transition 增加的屬性:
delay:規定在動畫開始之前的延遲。
duration:規定完成動畫所花費的時間,以秒或毫秒計。
count:規定動畫應該播放的次數。
easing:規定動畫的速度曲線。
reverse:規定是否應該輪流反向播放動畫。
四、優化
那么接下來我們來對 Transition 來進行一個優化,我們主要的是動畫監聽、卸載組件以及其他的相關兼容問題。那么我們在代碼中添加 props 屬性,并且設置默認值。代碼如下所示:
const propTypes = {
...,
/** 動畫結束的回調 */
onEnd: PropTypes.func,
/** 離開動畫結束時卸載元素 */
exist: PropTypes.bool
}
const defaultProps = {
...,
reverse: false,
exist: false
}
接下來進行動畫結束監聽的事件代碼:
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
...
onEnd = e => {
const { onEnd, action, exist } = this.props
if (onEnd) {
onEnd(e)
}
// 卸載 DOM 元素
if (!action && exist) {
const node = e.target.parentNode
node.parentNode.removeChild(node)
}
}
/**
* 對動畫結束事件 onEnd 回調的處理函數
*
* @param {string} type - 事件解綁定類型: add - 綁定事件,remove - 移除事件綁定
*/
handleEndListener (type = 'add') {
const el = ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')
const events = ['animationend', 'transitionend']
events.forEach(ev => {
el[`${type}EventListener`](ev, this.onEnd, false)
})
}
componentDidMount () {
this.handleEndListener()
}
componentWillUnmount () {
const { action, exist } = this.props
if (!action && exist) {
this.handleEndListener('remove')
}
}
render () {
...
}
}
在代碼中我們可以知道使用到 componentDidMount 和 componentWillUnmount 這兩個生命周期函數。
react-dom 中還為我們提供了可以在 React 應用中使用的 DOM 方法。我們通過獲取兼容性 animationend 和transitionend 事件。檢驗函數方法的代碼如下所示:
/**
* 瀏覽器兼容事件檢測函數
*
* @param {node} el - 觸發事件的 DOM 元素
* @param {array} events - 可能的事件類型
* @returns {*}
*/
const whichEvent = (el, events) => {
const len = events.length
for (var i = 0; i < len; i++) {
if (el.style[i]) {
return events[i];
}
}
}
修改 handleEndListener 函數代碼如下所示:
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
...
/**
* 對動畫結束事件 onEnd 回調的處理函數
*
* @param {string} type - 事件解綁定類型: add - 綁定事件,remove - 移除事件綁定
*/
handleEndListener (type = 'add') {
const el = ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')
const events = ['AnimationEnd', 'TransitionEnd']
events.forEach(ev => {
const eventType = whichEvent(el, [ev.toLowerCase(), `webkit${ev}`])
el[`${type}EventListener`](eventType, this.onEnd, false)
})
}
...
}
那么到這里之后我們就完成了整個 Transition 組件的開發,相關完整代碼如下所示:
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import ReactDOM from 'react-dom'
const propTypes = {
/** 執行動畫 */
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
}
const defaultProps = {
action: false,
count: 1,
reverse: false,
exist: false
}
/**
* 瀏覽器兼容事件檢測函數
*
* @param {node} el - 觸發事件的 DOM 元素
* @param {array} events - 可能的事件類型
* @returns {*}
*/
const whichEvent = (el, events) => {
const len = events.length
for (var i = 0; i < len; i++) {
if (el.style[i]) {
return events[i];
}
}
}
/**
* css過渡動畫組件
*
* @visibleName Transition 過渡動畫
*/
class Transition extends React.Component {
static propTypes = propTypes
static defaultProps = defaultProps
onEnd = e => {
const { onEnd, action, exist } = this.props
if (onEnd) {
onEnd(e)
}
// 卸載 DOM 元素
if (!action && exist) {
const node = e.target.parentNode
node.parentNode.removeChild(node)
}
}
/**
* 對動畫結束事件 onEnd 回調的處理函數
*
* @param {string} type - 事件解綁定類型: add - 綁定事件,remove - 移除事件綁定
*/
handleEndListener (type = 'add') {
const el = ReactDOM.findDOMNode(this).querySelector('.transition-wrapper')
const events = ['AnimationEnd', 'TransitionEnd']
events.forEach(ev => {
const eventType = whichEvent(el, [ev.toLowerCase(), `webkit${ev}`])
el[`${type}EventListener`](eventType, this.onEnd, false)
})
}
componentDidMount () {
this.handleEndListener()
}
componentWillUnmount() {
const { action, exist } = this.props
if (!action && exist) {
this.handleEndListener('remove')
}
}
render () {
const {
className,
action,
toggleClass,
enterClass,
leaveClass,
delay,
duration,
count,
easing,
reverse,
children
} = this.props
// 動畫樣式
const styleText = (() => {
let style = {}
// 設置延遲時長
if (delay) {
style.transitionDelay = delay
style.animationDelay = delay
}
// 設置播放時長
if (duration) {
style.transitionDuration = duration
style.animationDuration = duration
}
// 設置播放次數
if (count) {
style.animationIterationCount = count
}
// 設置緩動函數
if (easing) {
style.transitionTimingFunction = easing
style.animationTimingFunction = easing
}
// 設置動畫方向
if (reverse) {
style.animationDirection = 'alternate'
}
return style
})()
const transition = (
<div
className={
classnames({
transition: true
})
}
style={
{
position: 'relative',
overflow: 'hidden'
}
}
>
<div
className={
classnames({
'transition-wrapper': true,
[className]: className,
[toggleClass]: action && toggleClass,
[enterClass]: !toggleClass && action && enterClass,
[leaveClass]: !toggleClass && !action && leaveClass,
})
}
style={ styleText }
>
{ children }
</div>
</div>
)
return transition
}
}
export default Transition
關于“React怎么實現一個Transition過渡動畫組件”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。