在React中寫一個Animation組件為組件進(jìn)入和離開加上動畫/過度效果
問題
在單頁面應(yīng)用中,我們經(jīng)常需要給路由的切換或者元素的掛載和卸載加上過渡效果,為這么一個小功能引入第三方框架,實(shí)在有點(diǎn)小糾結(jié)。不如自己封裝。
思路
原理
以進(jìn)入時 opacity: 0 --> opacity: 1 ,退出時 opacity: 0 --> opacity: 1 為例
元素掛載時
	1.掛載元素dom
	2.設(shè)置動畫 opacity: 0 --> opacity: 1
元素卸載時
	1.設(shè)置動畫 opacity: 0 --> opacity: 1 
	2.動畫結(jié)束后卸載dom
組件設(shè)計(jì)
為了使得組件簡單易用、低耦合,我們期望如下方式來調(diào)用組件:
在 App.jsx 里調(diào)用組件:
通過改變isShow的值來指定是否顯示
	// App.jsx
	// 其他代碼省略
	import './app.css';
	<Animation isShow={isShow} name='demo'>
	    <div class='demo'>
	        demo
	    </div>
	</Animation>
	// 通過改變isShow的值來指定是否顯示
	在 App.css 里指定進(jìn)入離開效果:
	// 基礎(chǔ)樣式
	.demo {
	    width: 200px;
	    height: 200px;
	    background-color: red;
	}
	// 定義進(jìn)出入動畫
	.demo-showing {
	    animation: show 0.5s forwards;
	}
	.demo-fading {
	    animation: fade 0.5s forwards;
	}
	// 定義動畫fade與show
	@keyframes show {
	    from {
	        opacity: 0;
	    }
	    to {
	        opacity: 1;
	    }
	}
	@keyframes fade {
	    from {
	        opacity: 1;
	    }
	    to {
	        opacity: 0;
	    }
	}
根據(jù)思路寫代碼
	// Animation.jsx
	import { PureComponent } from 'react';
	import './index.css';
	class Animation extends PureComponent {
	    constructor(props) {
	        super(props);
	        this.state = {
	            isInnerShow: false,
	            animationClass: '',
	        };
	    }
	    componentWillReceiveProps(props) {
	        const { isShow } = props;
	        if (isShow) {
	            // 顯示
	            this.show().then(() => {
	                this.doShowAnimation();
	            });
	        } else {
	            // 隱藏
	            this.doFadeAnimation();
	        }
	    }
	    handleAnimationEnd() {
	        const isFading = this.state.animationClass === this.className('fading');
	        if (isFading) {
	            this.hide();
	        }
	    }
	    show() {
	        return new Promise(resolve => {
	            this.setState(
	                {
	                    isInnerShow: true,
	                },
	                () => {
	                    resolve();
	                }
	            );
	        });
	    }
	    hide() {
	        this.setState({
	            isInnerShow: false,
	        });
	    }
	    doShowAnimation() {
	        this.setState({
	            animationClass: this.className('showing'),
	        });
	    }
	    doFadeAnimation() {
	        this.setState({
	            animationClass: this.className('fading'),
	        });
	    }
	    /**
	     * 獲取className
	     * @param {string} inner 'showing' | 'fading'
	     */
	    className(inner) {
	        const { name } = this.props;
	        if (!name) throw new Error('animation name must be assigned');
	        return `${name}-${inner}`;
	    }
	    render() {
	        let { children } = this.props;
	        children = React.Children.only(children);
	        const { isInnerShow, animationClass } = this.state;
	        const element = {
	            ...children,
	            props: {
	                ...children.props,
	                className: `${children.props.className} ${animationClass}`,
	                onAnimationEnd: this.handleAnimationEnd.bind(this),
	            },
	        };
	        return isInnerShow && element;
	    }
	}
	export default Animation;
Demo示例
點(diǎn)我直達(dá)
新聞熱點(diǎn)
疑難解答